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

refactoring / fixes

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman 2020-07-11 19:07:06 +03:00
parent 186d69dfc6
commit 35f081eaf4
27 changed files with 148 additions and 155 deletions

View File

@ -1,3 +1,3 @@
import { helmCli } from "../src/main/helm-cli"
import { helmCli } from "../src/main/helm/helm-cli"
helmCli.ensureBinary()

View File

@ -23,7 +23,7 @@ export const htmlTemplate = path.resolve(rendererDir, "template.html");
export const sassCommonVars = path.resolve(rendererDir, "components/vars.scss");
// Apis
export const apiPrefix = "/api-local" // local router apis
export const apiPrefix = "/api" // local router apis
export const apiKubePrefix = "/api-kube" // k8s cluster apis
// Links

View File

@ -53,7 +53,7 @@ export class MetricsFeature extends Feature {
async install(cluster: Cluster): Promise<boolean> {
// Check if there are storageclasses
const storageClient = cluster.proxyKubeconfig().makeApiClient(k8s.StorageV1Api)
const storageClient = cluster.getProxyKubeconfig().makeApiClient(k8s.StorageV1Api)
const scs = await storageClient.listStorageClass();
scs.body.items.forEach(sc => {
if(sc.metadata.annotations &&
@ -93,9 +93,9 @@ export class MetricsFeature extends Feature {
async uninstall(cluster: Cluster): Promise<boolean> {
return new Promise<boolean>(async (resolve, reject) => {
const rbacClient = cluster.proxyKubeconfig().makeApiClient(RbacAuthorizationV1Api)
const rbacClient = cluster.getProxyKubeconfig().makeApiClient(RbacAuthorizationV1Api)
try {
await this.deleteNamespace(cluster.proxyKubeconfig(), "lens-metrics")
await this.deleteNamespace(cluster.getProxyKubeconfig(), "lens-metrics")
await rbacClient.deleteClusterRole("lens-prometheus");
await rbacClient.deleteClusterRoleBinding("lens-prometheus");
resolve(true);

View File

@ -37,7 +37,7 @@ export class UserModeFeature extends Feature {
async uninstall(cluster: Cluster): Promise<boolean> {
return new Promise<boolean>(async (resolve, reject) => {
const rbacClient = cluster.proxyKubeconfig().makeApiClient(RbacAuthorizationV1Api)
const rbacClient = cluster.getProxyKubeconfig().makeApiClient(RbacAuthorizationV1Api)
try {
await rbacClient.deleteClusterRole("lens-user");
await rbacClient.deleteClusterRoleBinding("lens-user");

View File

@ -25,13 +25,11 @@ export class ClusterManager {
return path.join(app.getPath("userData"), "icons");
}
constructor(public readonly proxyPort: number) {
constructor(public readonly port: number) {
// auto-init clusters
autorun(() => {
const freshClusters = clusterStore.clustersList.filter(cluster => !cluster.initialized);
freshClusters.forEach(cluster => {
cluster.init().then(() => cluster.refreshCluster());
});
freshClusters.forEach(cluster => cluster.init(port));
});
// auto-stop removed clusters
autorun(() => {
@ -89,7 +87,7 @@ export class ClusterManager {
}
}
// fixme
// fixme: verify
getClusterForRequest(req: http.IncomingMessage): Cluster {
let cluster: Cluster = null
@ -154,7 +152,7 @@ export class ClusterManager {
}
protected async refreshCluster(clusterId: ClusterId) {
await this.getCluster(clusterId)?.refreshCluster();
await this.getCluster(clusterId)?.refreshStatus();
}
static ipcListen(clusterManager: ClusterManager) {

View File

@ -1,12 +1,12 @@
import type { ClusterId, ClusterModel, ClusterPreferences } from "../common/cluster-store"
import type { FeatureStatusMap } from "./feature"
import { UrlWithStringQuery } from "url"
import { action, observable, toJS } from "mobx";
import { apiKubePrefix } from "../common/vars";
import { ContextHandler } from "./context-handler"
import { AuthorizationV1Api, CoreV1Api, KubeConfig, V1ResourceAttributes } from "@kubernetes/client-node"
import { Kubectl } from "./kubectl";
import { KubeconfigManager } from "./kubeconfig-manager"
import { getNodeWarningConditions, podHasIssues } from "./k8s"
import { getNodeWarningConditions, loadConfig, podHasIssues } from "./k8s"
import { getFeatures, installFeature, uninstallFeature, upgradeFeature } from "./feature-manager";
import request, { RequestPromiseOptions } from "request-promise-native"
import logger from "./logger"
@ -40,11 +40,11 @@ export class Cluster implements ClusterModel {
@observable contextName: string;
@observable workspace: string;
@observable kubeConfigPath: string;
@observable port: number;
@observable url: string; // cluster-api url
@observable apiUrl: UrlWithStringQuery; // same as url, but parsed
@observable kubeAuthProxyUrl: string;
@observable proxyUrl: string; // lens-proxy url
@observable kubeProxyUrl: string;
@observable webContentUrl: string;
@observable proxyPort: number;
@observable online: boolean;
@observable accessible: boolean;
@observable failureReason: string;
@ -68,26 +68,27 @@ export class Cluster implements ClusterModel {
}
@action
async init() {
async init(proxyPort: number) {
try {
// fixme
this.proxyPort = proxyPort;
this.contextHandler = new ContextHandler(this);
this.port = await this.contextHandler.ensurePort(); // resolve port before KubeconfigManager
this.kubeAuthProxyUrl = `http://127.0.0.1:${this.port}`;
this.kubeconfigManager = new KubeconfigManager(this);
this.kubeconfigManager = new KubeconfigManager(this, this.contextHandler);
// this.url = this.kubeconfigManager.getCurrentClusterServer();
// this.apiUrl = url.parse(this.url);
this.webContentUrl = `http://${this.id}.localhost:${this.port}`;
this.url = this.getKubeconfig().getCurrentCluster().server;
this.proxyUrl = `http://localhost:${proxyPort}`;
this.kubeProxyUrl = this.proxyUrl + apiKubePrefix;
this.webContentUrl = `http://${this.id}.localhost:${proxyPort}`;
await this.refreshStatus();
this.initialized = true;
logger.info(`[CLUSTER]: init success`, {
id: this.id,
port: this.port,
url: this.url,
proxyUrl: this.proxyUrl,
kubeProxyUrl: this.kubeProxyUrl,
webContentUrl: this.webContentUrl,
kubeAuthProxyUrl: this.kubeAuthProxyUrl,
});
this.initialized = true;
} catch (err) {
logger.error(`[CLUSTER]: init error`, {
id: this.id,
@ -102,15 +103,11 @@ export class Cluster implements ClusterModel {
this.kubeconfigManager.unlink();
}
// todo: auto-refresh when preferences changed + by timer?
@action
async refreshCluster() {
this.contextHandler.setupPrometheus(this.preferences);
const connectionStatus = await this.getConnectionStatus()
this.accessible = connectionStatus == ClusterStatus.AccessGranted;
async refreshStatus() {
const connectionStatus = await this.getConnectionStatus();
this.online = connectionStatus > ClusterStatus.Offline;
this.accessible = connectionStatus == ClusterStatus.AccessGranted;
if (this.accessible) {
this.distribution = this.detectKubernetesDistribution(this.version)
this.features = await getFeatures(this)
@ -122,29 +119,31 @@ export class Cluster implements ClusterModel {
this.eventCount = await this.getEventCount();
}
proxyKubeconfigPath() {
return this.kubeconfigManager.getPath()
protected getKubeconfig(): KubeConfig {
return loadConfig(this.kubeConfigPath);
}
proxyKubeconfig() {
const kc = new KubeConfig()
kc.loadFromFile(this.proxyKubeconfigPath())
return kc
getProxyKubeconfig(): KubeConfig {
return loadConfig(this.getProxyKubeconfigPath());
}
getProxyKubeconfigPath(): string {
return this.kubeconfigManager.getPath()
}
async installFeature(name: string, config: any) {
await installFeature(name, this, config)
await this.refreshCluster()
await this.refreshStatus()
}
async upgradeFeature(name: string, config: any) {
await upgradeFeature(name, this, config)
await this.refreshCluster()
await this.refreshStatus()
}
async uninstallFeature(name: string) {
await uninstallFeature(name, this)
await this.refreshCluster()
await this.refreshStatus()
}
getPrometheusApiPrefix() {
@ -152,12 +151,12 @@ export class Cluster implements ClusterModel {
}
k8sRequest(path: string, options: RequestPromiseOptions = {}) {
return request(this.kubeAuthProxyUrl + path, {
return request(this.kubeProxyUrl + path, {
json: true,
timeout: 10000,
headers: {
...(options.headers || {}),
host: `${this.id}.localhost:${this.port}`,
host: `${this.id}.localhost:${this.proxyPort}`,
}
})
}
@ -193,7 +192,7 @@ export class Cluster implements ClusterModel {
}
async canI(resourceAttributes: V1ResourceAttributes): Promise<boolean> {
const authApi = this.proxyKubeconfig().makeApiClient(AuthorizationV1Api)
const authApi = this.getProxyKubeconfig().makeApiClient(AuthorizationV1Api)
try {
const accessReview = await authApi.createSelfSubjectAccessReview({
apiVersion: "authorization.k8s.io/v1",
@ -240,7 +239,7 @@ export class Cluster implements ClusterModel {
if (!this.isAdmin) {
return 0;
}
const client = this.proxyKubeconfig().makeApiClient(CoreV1Api);
const client = this.getProxyKubeconfig().makeApiClient(CoreV1Api);
try {
const response = await client.listEventForAllNamespaces(false, null, null, null, 1000);
const uniqEventSources = new Set();

View File

@ -2,26 +2,27 @@ import type { PrometheusProvider, PrometheusService } from "./prometheus/provide
import type { ClusterPreferences } from "../common/cluster-store";
import type { Cluster } from "./cluster"
import type httpProxy from "http-proxy"
import url, { UrlWithStringQuery } from "url";
import { CoreV1Api } from "@kubernetes/client-node"
import { observable } from "mobx";
import { prometheusProviders } from "../common/prometheus-providers"
import logger from "./logger"
import { getFreePort } from "./port"
import { KubeAuthProxy } from "./kube-auth-proxy"
export class ContextHandler {
@observable proxyPort: number;
public proxyPort: number;
public clusterUrl: UrlWithStringQuery;
protected proxyServer: KubeAuthProxy
protected apiTarget: httpProxy.ServerOptions
protected prometheusProvider: string
protected prometheusPath: string
constructor(protected cluster: Cluster) {
this.setupPrometheus(cluster.preferences)
this.clusterUrl = url.parse(cluster.url);
this.setupPrometheus(cluster.preferences);
}
public setupPrometheus(preferences: ClusterPreferences = {}) {
protected setupPrometheus(preferences: ClusterPreferences = {}) {
this.prometheusProvider = preferences.prometheusProvider?.type;
this.prometheusPath = null;
if (preferences.prometheus) {
@ -47,7 +48,7 @@ export class ContextHandler {
public async getPrometheusService(): Promise<PrometheusService> {
const providers = this.prometheusProvider ? prometheusProviders.filter(provider => provider.id == this.prometheusProvider) : prometheusProviders;
const prometheusPromises: Promise<PrometheusService>[] = providers.map(async (provider: PrometheusProvider): Promise<PrometheusService> => {
const apiClient = this.cluster.proxyKubeconfig().makeApiClient(CoreV1Api)
const apiClient = this.cluster.getProxyKubeconfig().makeApiClient(CoreV1Api)
return await provider.getPrometheusService(apiClient)
})
const resolvedPrometheusServices = await Promise.all(prometheusPromises)
@ -79,10 +80,10 @@ export class ContextHandler {
return apiTarget
}
// fixme: verify
public async getApiTargetUrl(): Promise<string> {
const port = await this.ensurePort();
const { path } = this.cluster.apiUrl;
return `http://127.0.0.1:${port}${path}`;
await this.ensurePort();
return `http://127.0.0.1:${this.proxyPort}${this.clusterUrl.path}`;
}
protected async newApiTarget(timeout: number): Promise<httpProxy.ServerOptions> {
@ -91,7 +92,7 @@ export class ContextHandler {
target: await this.getApiTargetUrl(),
timeout: timeout,
headers: {
"Host": this.cluster.apiUrl.hostname,
"Host": this.clusterUrl.hostname,
}
}
}

View File

@ -20,7 +20,7 @@ export async function getFeatures(cluster: Cluster): Promise<FeatureStatusMap> {
logger.debug("getting feature status...");
const feature = ALL_FEATURES[key] as Feature;
const kc = new KubeConfig()
kc.loadFromFile(cluster.proxyKubeconfigPath())
kc.loadFromFile(cluster.getProxyKubeconfigPath())
const status = await feature.featureStatus(kc);
result[feature.name] = status

View File

@ -1,8 +1,8 @@
import fs from "fs";
import * as yaml from "js-yaml";
import { HelmRepo, HelmRepoManager } from "./helm-repo-manager"
import logger from "./logger";
import { promiseExec } from "./promise-exec"
import logger from "../logger";
import { promiseExec } from "../promise-exec"
import { helmCli } from "./helm-cli"
type CachedYaml = {

View File

@ -1,7 +1,7 @@
import packageInfo from "../../package.json"
import packageInfo from "../../../package.json"
import path from "path"
import { LensBinary, LensBinaryOpts } from "./lens-binary"
import { isProduction } from "../common/vars";
import { LensBinary, LensBinaryOpts } from "../lens-binary"
import { isProduction } from "../../common/vars";
export class HelmCli extends LensBinary {

View File

@ -1,10 +1,10 @@
import * as tempy from "tempy";
import fs from "fs";
import * as yaml from "js-yaml";
import { promiseExec} from "./promise-exec"
import { promiseExec} from "../promise-exec"
import { helmCli } from "./helm-cli";
import { Cluster } from "./cluster";
import { toCamelCase } from "../common/utils/camelCase";
import { Cluster } from "../cluster";
import { toCamelCase } from "../../common/utils/camelCase";
export class HelmReleaseManager {
@ -54,7 +54,7 @@ export class HelmReleaseManager {
await fs.promises.writeFile(fileName, yaml.safeDump(values))
try {
const { stdout, stderr } = await promiseExec(`"${helm}" upgrade ${name} ${chart} --version ${version} -f ${fileName} --namespace ${namespace} --kubeconfig ${cluster.proxyKubeconfigPath()}`).catch((error) => { throw(error.stderr)})
const { stdout, stderr } = await promiseExec(`"${helm}" upgrade ${name} ${chart} --version ${version} -f ${fileName} --namespace ${namespace} --kubeconfig ${cluster.getProxyKubeconfigPath()}`).catch((error) => { throw(error.stderr)})
return {
log: stdout,
release: this.getRelease(name, namespace, cluster)
@ -66,7 +66,7 @@ export class HelmReleaseManager {
public async getRelease(name: string, namespace: string, cluster: Cluster) {
const helm = await helmCli.binaryPath()
const {stdout, stderr} = await promiseExec(`"${helm}" status ${name} --output json --namespace ${namespace} --kubeconfig ${cluster.proxyKubeconfigPath()}`).catch((error) => { throw(error.stderr)})
const {stdout, stderr} = await promiseExec(`"${helm}" status ${name} --output json --namespace ${namespace} --kubeconfig ${cluster.getProxyKubeconfigPath()}`).catch((error) => { throw(error.stderr)})
const release = JSON.parse(stdout)
release.resources = await this.getResources(name, namespace, cluster)
return release
@ -100,7 +100,7 @@ export class HelmReleaseManager {
protected async getResources(name: string, namespace: string, cluster: Cluster) {
const helm = await helmCli.binaryPath()
const kubectl = await cluster.kubeCtl.getPath()
const pathToKubeconfig = cluster.proxyKubeconfigPath()
const pathToKubeconfig = cluster.getProxyKubeconfigPath()
const { stdout } = await promiseExec(`"${helm}" get manifest ${name} --namespace ${namespace} --kubeconfig ${pathToKubeconfig} | "${kubectl}" get -n ${namespace} --kubeconfig ${pathToKubeconfig} -f - -o=json`).catch((error) => {
return { stdout: JSON.stringify({items: []})}
})

View File

@ -1,9 +1,9 @@
import fs from "fs";
import logger from "./logger";
import logger from "../logger";
import * as yaml from "js-yaml";
import { promiseExec } from "./promise-exec";
import { promiseExec } from "../promise-exec";
import { helmCli } from "./helm-cli";
import { Singleton } from "../common/utils/singleton";
import { Singleton } from "../../common/utils/singleton";
export type HelmEnv = Record<string, string> & {
HELM_REPOSITORY_CACHE?: string;
@ -125,7 +125,6 @@ export class HelmRepoManager extends Singleton {
logger.error(error)
}
})
}
public async addRepo(repository: HelmRepo) {

View File

@ -1,13 +1,12 @@
import { Cluster } from "./cluster";
import logger from "./logger";
import { Cluster } from "../cluster";
import logger from "../logger";
import { repoManager } from "./helm-repo-manager";
import { HelmChartManager } from "./helm-chart-manager";
import { releaseManager } from "./helm-release-manager";
class HelmService {
public async installChart(cluster: Cluster, data: {chart: string; values: {}; name: string; namespace: string; version: string}) {
const installResult = await releaseManager.installChart(data.chart, data.values, data.name, data.namespace, data.version, cluster.proxyKubeconfigPath())
return installResult
public async installChart(cluster: Cluster, data: { chart: string; values: {}; name: string; namespace: string; version: string }) {
return await releaseManager.installChart(data.chart, data.values, data.name, data.namespace, data.version, cluster.getProxyKubeconfigPath())
}
public async listCharts() {
@ -19,7 +18,7 @@ class HelmService {
const manager = new HelmChartManager(repo)
let entries = await manager.charts()
entries = this.excludeDeprecated(entries)
for(const key in entries) {
for (const key in entries) {
entries[key] = entries[key][0]
}
charts[repo.name] = entries
@ -48,50 +47,44 @@ class HelmService {
public async listReleases(cluster: Cluster, namespace: string = null) {
await repoManager.init()
const releases = await releaseManager.listReleases(cluster.proxyKubeconfigPath(), namespace)
return releases
return await releaseManager.listReleases(cluster.getProxyKubeconfigPath(), namespace)
}
public async getRelease(cluster: Cluster, releaseName: string, namespace: string) {
public async getRelease(cluster: Cluster, releaseName: string, namespace: string) {
logger.debug("Fetch release")
const release = await releaseManager.getRelease(releaseName, namespace, cluster)
return release
return await releaseManager.getRelease(releaseName, namespace, cluster)
}
public async getReleaseValues(cluster: Cluster, releaseName: string, namespace: string) {
logger.debug("Fetch release values")
const values = await releaseManager.getValues(releaseName, namespace, cluster.proxyKubeconfigPath())
return values
return await releaseManager.getValues(releaseName, namespace, cluster.getProxyKubeconfigPath())
}
public async getReleaseHistory(cluster: Cluster, releaseName: string, namespace: string) {
logger.debug("Fetch release history")
const history = await releaseManager.getHistory(releaseName, namespace, cluster.proxyKubeconfigPath())
return(history)
return await releaseManager.getHistory(releaseName, namespace, cluster.getProxyKubeconfigPath())
}
public async deleteRelease(cluster: Cluster, releaseName: string, namespace: string) {
logger.debug("Delete release")
const release = await releaseManager.deleteRelease(releaseName, namespace, cluster.proxyKubeconfigPath())
return release
return await releaseManager.deleteRelease(releaseName, namespace, cluster.getProxyKubeconfigPath())
}
public async updateRelease(cluster: Cluster, releaseName: string, namespace: string, data: {chart: string; values: {}; version: string}) {
public async updateRelease(cluster: Cluster, releaseName: string, namespace: string, data: { chart: string; values: {}; version: string }) {
logger.debug("Upgrade release")
const release = await releaseManager.upgradeRelease(releaseName, data.chart, data.values, namespace, data.version, cluster)
return release
return await releaseManager.upgradeRelease(releaseName, data.chart, data.values, namespace, data.version, cluster)
}
public async rollback(cluster: Cluster, releaseName: string, namespace: string, revision: number) {
logger.debug("Rollback release")
const output = await releaseManager.rollback(releaseName, namespace, revision, cluster.proxyKubeconfigPath())
return({ message: output })
const output = await releaseManager.rollback(releaseName, namespace, revision, cluster.getProxyKubeconfigPath())
return { message: output }
}
protected excludeDeprecated(entries: any) {
for(const key in entries) {
for (const key in entries) {
entries[key] = entries[key].filter((entry: any) => {
if(Array.isArray(entry)) {
if (Array.isArray(entry)) {
return entry[0]['deprecated'] != true
}
return entry["deprecated"] != true

View File

@ -1,3 +1,4 @@
import type { ContextHandler } from "./context-handler";
import type { Cluster } from "./cluster"
import { app } from "electron"
import fs from "fs-extra"
@ -9,11 +10,12 @@ export class KubeconfigManager {
protected configDir = app.getPath("temp")
protected tempFile: string
constructor(protected cluster: Cluster) {
constructor(protected cluster: Cluster, protected contextHandler: ContextHandler) {
this.init();
}
protected async init() {
await this.contextHandler.ensurePort();
this.tempFile = await this.createTemporaryKubeconfig();
}
@ -28,12 +30,12 @@ export class KubeconfigManager {
protected async createTemporaryKubeconfig(): Promise<string> {
fs.ensureDir(this.configDir);
const path = `${this.configDir}/${randomFileName("kubeconfig")}`;
const { contextName, contextHandler, kubeConfigPath } = this.cluster;
const { contextName, kubeConfigPath } = this.cluster;
const kubeConfig = loadConfig(kubeConfigPath);
kubeConfig.clusters = [
{
name: contextName,
server: await contextHandler.getApiTargetUrl(),
server: await this.contextHandler.getApiTargetUrl(),
skipTLSVerify: true,
}
];

View File

@ -7,7 +7,7 @@ import logger from "./logger"
import { ensureDir, pathExists } from "fs-extra"
import { globalRequestOpts } from "../common/request"
import * as lockFile from "proper-lockfile"
import { helmCli } from "./helm-cli"
import { helmCli } from "./helm/helm-cli"
import { userStore } from "../common/user-store"
import { getBundledKubectlVersion} from "../common/utils/app-version"

View File

@ -26,11 +26,9 @@ export class LensProxy {
this.router = new Router();
}
listen(): this {
const proxyServer = this.buildCustomProxy();
const { proxyPort } = this.clusterManager;
this.proxyServer = proxyServer.listen(proxyPort);
logger.info(`LensProxy server has started http://localhost:${proxyPort}`);
listen(port = this.clusterManager.port): this {
this.proxyServer = this.buildCustomProxy().listen(port);
logger.info(`LensProxy server has started http://localhost:${port}`);
return this;
}
@ -71,10 +69,10 @@ export class LensProxy {
if (req.method !== "GET") {
return
}
const reqUrl = `${req.headers.host}${req.url}`
if (this.retryCounters.has(reqUrl)) {
logger.debug("Resetting proxy retry cache for url: " + reqUrl)
this.retryCounters.delete(reqUrl)
const reqId = this.getRequestId(req);
if (this.retryCounters.has(reqId)) {
logger.debug(`Resetting proxy retry cache for url: ${reqId}`);
this.retryCounters.delete(reqId)
}
})
proxy.on("error", (error, req, res, target) => {
@ -84,13 +82,13 @@ export class LensProxy {
if (target) {
logger.debug("Failed proxy to target: " + JSON.stringify(target, null, 2));
if (req.method === "GET" && (!res.statusCode || res.statusCode >= 500)) {
const retryCounterKey = `${req.headers.host}${req.url}`
const retryCount = this.retryCounters.get(retryCounterKey) || 0
const reqId = this.getRequestId(req);
const retryCount = this.retryCounters.get(reqId) || 0
const timeoutMs = retryCount * 250
if (retryCount < 20) {
logger.debug("Retrying proxy request to url: " + retryCounterKey)
logger.debug(`Retrying proxy request to url: ${reqId}`)
setTimeout(() => {
this.retryCounters.set(retryCounterKey, retryCount + 1)
this.retryCounters.set(reqId, retryCount + 1)
this.handleRequest(proxy, req, res)
}, timeoutMs)
}
@ -108,13 +106,12 @@ export class LensProxy {
protected createWsListener(): WebSocket.Server {
const ws = new WebSocket.Server({ noServer: true })
return ws.on("connection", (async (socket: WebSocket, req: http.IncomingMessage) => {
const cluster = this.clusterManager.getClusterForRequest(req)
const cluster = this.clusterManager.getClusterForRequest(req);
const nodeParam = url.parse(req.url, true).query["node"]?.toString();
await nodeShell.open(socket, cluster, nodeParam);
}));
}
// fixme: remove api prefix?
protected async getProxyTarget(req: http.IncomingMessage, contextHandler: ContextHandler): Promise<httpProxy.ServerOptions> {
if (req.url.startsWith(apiKubePrefix)) {
delete req.headers.authorization
@ -124,11 +121,15 @@ export class LensProxy {
}
}
protected getRequestId(req: http.IncomingMessage) {
return req.headers.host + req.url;
}
protected async handleRequest(proxy: httpProxy, req: http.IncomingMessage, res: http.ServerResponse) {
const cluster = this.clusterManager.getClusterForRequest(req)
if (!cluster) {
logger.error("Got request to unknown cluster")
logger.debug(req.headers.host + req.url)
const reqId = this.getRequestId(req);
logger.error("Got request to unknown cluster", { reqId })
res.statusCode = 503
res.end()
return

View File

@ -17,7 +17,7 @@ export class NodeShellSession extends ShellSession {
super(socket, cluster)
this.nodeName = nodeName
this.podId = `node-shell-${uuid()}`
this.kc = cluster.proxyKubeconfig()
this.kc = cluster.getProxyKubeconfig()
}
public async open() {

View File

@ -4,10 +4,8 @@ import http from "http"
import path from "path"
import { readFile, stat } from "fs-extra"
import { Cluster } from "./cluster"
import { helmApi } from "./helm-api"
import { resourceApplierApi } from "./resource-applier-api"
import { apiPrefix, appName, outDir } from "../common/vars";
import { configRoute, kubeconfigRoute, metricsRoute, portForwardRoute, watchRoute } from "./routes";
import { configRoute, helmRoute, kubeconfigRoute, metricsRoute, portForwardRoute, resourceApplierRoute, watchRoute } from "./routes";
export interface RouterRequestOpts {
req: http.IncomingMessage;
@ -129,20 +127,20 @@ export class Router {
this.router.add({ method: "post", path: `${apiPrefix}/services/{namespace}/{service}/port-forward/{port}` }, portForwardRoute.routeServicePortForward.bind(portForwardRoute))
// Helm API
this.router.add({ method: "get", path: `${apiPrefix}/v2/charts` }, helmApi.listCharts.bind(helmApi))
this.router.add({ method: "get", path: `${apiPrefix}/v2/charts/{repo}/{chart}` }, helmApi.getChart.bind(helmApi))
this.router.add({ method: "get", path: `${apiPrefix}/v2/charts/{repo}/{chart}/values` }, helmApi.getChartValues.bind(helmApi))
this.router.add({ method: "get", path: `${apiPrefix}/v2/charts` }, helmRoute.listCharts.bind(helmRoute))
this.router.add({ method: "get", path: `${apiPrefix}/v2/charts/{repo}/{chart}` }, helmRoute.getChart.bind(helmRoute))
this.router.add({ method: "get", path: `${apiPrefix}/v2/charts/{repo}/{chart}/values` }, helmRoute.getChartValues.bind(helmRoute))
this.router.add({ method: "post", path: `${apiPrefix}/v2/releases` }, helmApi.installChart.bind(helmApi))
this.router.add({ method: `put`, path: `${apiPrefix}/v2/releases/{namespace}/{release}` }, helmApi.updateRelease.bind(helmApi))
this.router.add({ method: `put`, path: `${apiPrefix}/v2/releases/{namespace}/{release}/rollback` }, helmApi.rollbackRelease.bind(helmApi))
this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace?}` }, helmApi.listReleases.bind(helmApi))
this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace}/{release}` }, helmApi.getRelease.bind(helmApi))
this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace}/{release}/values` }, helmApi.getReleaseValues.bind(helmApi))
this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace}/{release}/history` }, helmApi.getReleaseHistory.bind(helmApi))
this.router.add({ method: "delete", path: `${apiPrefix}/v2/releases/{namespace}/{release}` }, helmApi.deleteRelease.bind(helmApi))
this.router.add({ method: "post", path: `${apiPrefix}/v2/releases` }, helmRoute.installChart.bind(helmRoute))
this.router.add({ method: `put`, path: `${apiPrefix}/v2/releases/{namespace}/{release}` }, helmRoute.updateRelease.bind(helmRoute))
this.router.add({ method: `put`, path: `${apiPrefix}/v2/releases/{namespace}/{release}/rollback` }, helmRoute.rollbackRelease.bind(helmRoute))
this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace?}` }, helmRoute.listReleases.bind(helmRoute))
this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace}/{release}` }, helmRoute.getRelease.bind(helmRoute))
this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace}/{release}/values` }, helmRoute.getReleaseValues.bind(helmRoute))
this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace}/{release}/history` }, helmRoute.getReleaseHistory.bind(helmRoute))
this.router.add({ method: "delete", path: `${apiPrefix}/v2/releases/{namespace}/{release}` }, helmRoute.deleteRelease.bind(helmRoute))
// Resource Applier API
this.router.add({ method: "post", path: `${apiPrefix}/stack` }, resourceApplierApi.applyResource.bind(resourceApplierApi))
this.router.add({ method: "post", path: `${apiPrefix}/stack` }, resourceApplierRoute.applyResource.bind(resourceApplierRoute))
}
}

View File

@ -45,7 +45,7 @@ const apiResources = [
]
async function getAllowedNamespaces(cluster: Cluster) {
const api = cluster.proxyKubeconfig().makeApiClient(CoreV1Api)
const api = cluster.getProxyKubeconfig().makeApiClient(CoreV1Api)
try {
const namespaceList = await api.listNamespace()
const nsAccessStatuses = await Promise.all(
@ -59,7 +59,7 @@ async function getAllowedNamespaces(cluster: Cluster) {
.filter((ns, i) => nsAccessStatuses[i])
.map(ns => ns.metadata.name)
} catch(error) {
const ctx = cluster.proxyKubeconfig().getContextObject(cluster.contextName)
const ctx = cluster.getProxyKubeconfig().getContextObject(cluster.contextName)
if (ctx.namespace) {
return [ctx.namespace]
}

View File

@ -1,9 +1,9 @@
import { LensApiRequest } from "./router"
import { helmService } from "./helm-service"
import { LensApi } from "./lens-api"
import logger from "./logger"
import { LensApiRequest } from "../router"
import { helmService } from "../helm/helm-service"
import { LensApi } from "../lens-api"
import logger from "../logger"
class HelmApi extends LensApi {
class HelmApiRoute extends LensApi {
public async listCharts(request: LensApiRequest) {
const { response } = request
const charts = await helmService.listCharts()
@ -111,4 +111,4 @@ class HelmApi extends LensApi {
}
}
export const helmApi = new HelmApi()
export const helmRoute = new HelmApiRoute()

View File

@ -2,4 +2,6 @@ export * from "./config-route"
export * from "./kubeconfig-route"
export * from "./metrics-route"
export * from "./port-forward-route"
export * from "./watch-route"
export * from "./watch-route"
export * from "./helm-route"
export * from "./resource-applier-route"

View File

@ -44,7 +44,7 @@ class KubeconfigRoute extends LensApi {
public async routeServiceAccountRoute(request: LensApiRequest) {
const { params, response, cluster} = request
const client = cluster.proxyKubeconfig().makeApiClient(CoreV1Api);
const client = cluster.getProxyKubeconfig().makeApiClient(CoreV1Api);
const secretList = await client.listNamespacedSecret(params.namespace)
const secret = secretList.body.items.find(secret => {
const { annotations } = secret.metadata;

View File

@ -87,7 +87,7 @@ class PortForwardRoute extends LensApi {
namespace: params.namespace,
name: params.service,
port: params.port,
kubeConfig: cluster.proxyKubeconfigPath()
kubeConfig: cluster.getProxyKubeconfigPath()
})
const started = await portForward.start()
if (!started) {

View File

@ -1,8 +1,8 @@
import { LensApiRequest } from "./router"
import { ResourceApplier } from "./resource-applier"
import { LensApi } from "./lens-api"
import { LensApiRequest } from "../router"
import { LensApi } from "../lens-api"
import { ResourceApplier } from "../resource-applier"
class ResourceApplierApi extends LensApi {
class ResourceApplierApiRoute extends LensApi {
public async applyResource(request: LensApiRequest) {
const { response, cluster, payload } = request
try {
@ -14,4 +14,4 @@ class ResourceApplierApi extends LensApi {
}
}
export const resourceApplierApi = new ResourceApplierApi()
export const resourceApplierRoute = new ResourceApplierApiRoute()

View File

@ -87,10 +87,10 @@ class WatchRoute extends LensApi {
response.setHeader("Content-Type", "text/event-stream")
response.setHeader("Cache-Control", "no-cache")
response.setHeader("Connection", "keep-alive")
logger.debug("watch using kubeconfig:" + JSON.stringify(cluster.proxyKubeconfig(), null, 2))
logger.debug("watch using kubeconfig:" + JSON.stringify(cluster.getProxyKubeconfig(), null, 2))
apis.forEach(apiUrl => {
const watcher = new ApiWatcher(apiUrl, cluster.proxyKubeconfig(), response)
const watcher = new ApiWatcher(apiUrl, cluster.getProxyKubeconfig(), response)
watcher.start()
watchers.push(watcher)
})

View File

@ -7,7 +7,7 @@ import { app } from "electron"
import { Kubectl } from "./kubectl"
import { Cluster } from "./cluster"
import { ClusterPreferences } from "../common/cluster-store";
import { helmCli } from "./helm-cli"
import { helmCli } from "./helm/helm-cli"
import { isWindows } from "../common/vars";
import { tracker } from "../common/tracker";

View File

@ -1,6 +1,6 @@
import Vue from "vue"
import { MutationTree, ActionTree, GetterTree } from "vuex"
import { HelmRepo, repoManager } from "../../../../main/helm-repo-manager"
import { HelmRepo, repoManager } from "../../../../main/helm/helm-repo-manager"
export interface HelmRepoState {
repos: HelmRepo[];