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:
parent
186d69dfc6
commit
35f081eaf4
@ -1,3 +1,3 @@
|
||||
import { helmCli } from "../src/main/helm-cli"
|
||||
import { helmCli } from "../src/main/helm/helm-cli"
|
||||
|
||||
helmCli.ensureBinary()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 = {
|
||||
@ -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 {
|
||||
|
||||
@ -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: []})}
|
||||
})
|
||||
@ -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) {
|
||||
@ -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
|
||||
@ -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,
|
||||
}
|
||||
];
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@ -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]
|
||||
}
|
||||
|
||||
@ -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()
|
||||
@ -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"
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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()
|
||||
@ -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)
|
||||
})
|
||||
|
||||
@ -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";
|
||||
|
||||
|
||||
@ -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[];
|
||||
|
||||
Loading…
Reference in New Issue
Block a user