mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
1 parent
24f754697b
commit
6fd9b7cf76
14 files changed
+452
-535
No files matched your search
@@ -6,12 +6,13 @@ import { promiseExec } from "../promise-exec"
|
||||
import { helmCli } from "./helm-cli"
|
||||
|
||||
type CachedYaml = {
|
||||
entries: any;
|
||||
entries: any; // todo: types
|
||||
}
|
||||
|
||||
export class HelmChartManager {
|
||||
protected cache: any
|
||||
protected cache: any = {}
|
||||
protected repo: HelmRepo
|
||||
|
||||
constructor(repo: HelmRepo){
|
||||
this.cache = HelmRepoManager.cache
|
||||
this.repo = repo
|
||||
|
||||
@@ -1,28 +1,51 @@
|
||||
import fs from "fs";
|
||||
import logger from "../logger";
|
||||
import * as yaml from "js-yaml";
|
||||
import yaml from "js-yaml";
|
||||
import { readFile } from "fs-extra";
|
||||
import { promiseExec } from "../promise-exec";
|
||||
import { helmCli } from "./helm-cli";
|
||||
import { Singleton } from "../../common/utils/singleton";
|
||||
import { customRequestPromise } from "../../common/request";
|
||||
import orderBy from "lodash/orderBy";
|
||||
import logger from "../logger";
|
||||
|
||||
export type HelmEnv = Record<string, string> & {
|
||||
HELM_REPOSITORY_CACHE?: string;
|
||||
HELM_REPOSITORY_CONFIG?: string;
|
||||
}
|
||||
|
||||
export type HelmRepo = {
|
||||
export interface HelmRepoConfig {
|
||||
repositories: HelmRepo[]
|
||||
}
|
||||
|
||||
export interface HelmRepo {
|
||||
name: string;
|
||||
url: string;
|
||||
cacheFilePath?: string;
|
||||
cacheFilePath?: string
|
||||
caFile?: string,
|
||||
certFile?: string,
|
||||
insecure_skip_tls_verify?: boolean,
|
||||
keyFile?: string,
|
||||
username?: string,
|
||||
password?: string,
|
||||
}
|
||||
|
||||
export class HelmRepoManager extends Singleton {
|
||||
static cache = {}
|
||||
static cache = {} // todo: remove implicit updates in helm-chart-manager.ts
|
||||
|
||||
protected repos: HelmRepo[];
|
||||
protected helmEnv: HelmEnv
|
||||
protected initialized: boolean
|
||||
|
||||
public async init() {
|
||||
async loadAvailableRepos(): Promise<HelmRepo[]> {
|
||||
const res = await customRequestPromise({
|
||||
uri: "https://hub.helm.sh/assets/js/repos.json",
|
||||
json: true,
|
||||
resolveWithFullResponse: true,
|
||||
timeout: 10000,
|
||||
});
|
||||
return orderBy<HelmRepo>(res.body.data, repo => repo.name);
|
||||
}
|
||||
|
||||
async init() {
|
||||
await helmCli.ensureBinary();
|
||||
if (!this.initialized) {
|
||||
this.helmEnv = await this.parseHelmEnv()
|
||||
@@ -47,60 +70,57 @@ export class HelmRepoManager extends Singleton {
|
||||
return env
|
||||
}
|
||||
|
||||
public async repositories(): Promise<Array<HelmRepo>> {
|
||||
public async repositories(): Promise<HelmRepo[]> {
|
||||
if (!this.initialized) {
|
||||
await this.init()
|
||||
}
|
||||
const repositoryFilePath = this.helmEnv.HELM_REPOSITORY_CONFIG
|
||||
const repoFile = await fs.promises.readFile(repositoryFilePath, 'utf8').catch(async (error) => {
|
||||
return null
|
||||
})
|
||||
if (!repoFile) {
|
||||
await this.addRepo({ name: "stable", url: "https://kubernetes-charts.storage.googleapis.com/" })
|
||||
return await this.repositories()
|
||||
}
|
||||
try {
|
||||
const repositories = yaml.safeLoad(repoFile)
|
||||
const result = repositories['repositories'].map((repository: HelmRepo) => {
|
||||
return {
|
||||
name: repository.name,
|
||||
url: repository.url,
|
||||
cacheFilePath: `${this.helmEnv.HELM_REPOSITORY_CACHE}/${repository['name']}-index.yaml`
|
||||
}
|
||||
});
|
||||
if (result.length == 0) {
|
||||
await this.addRepo({ name: "stable", url: "https://kubernetes-charts.storage.googleapis.com/" })
|
||||
return await this.repositories()
|
||||
const repoConfigFile = this.helmEnv.HELM_REPOSITORY_CONFIG;
|
||||
const { repositories }: HelmRepoConfig = await readFile(repoConfigFile, 'utf8')
|
||||
.then((yamlContent: string) => yaml.safeLoad(yamlContent))
|
||||
.catch(() => ({
|
||||
repositories: []
|
||||
}));
|
||||
if (!repositories.length) {
|
||||
await this.addRepo({ name: "stable", url: "https://kubernetes-charts.storage.googleapis.com/" });
|
||||
return await this.repositories();
|
||||
}
|
||||
return result
|
||||
return repositories.map(repo => ({
|
||||
...repo,
|
||||
cacheFilePath: `${this.helmEnv.HELM_REPOSITORY_CACHE}/${repo.name}-index.yaml`
|
||||
}));
|
||||
} catch (error) {
|
||||
logger.debug(error)
|
||||
logger.error(`[HELM]: repositories listing error "${error}"`)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
public async repository(name: string) {
|
||||
const repositories = await this.repositories()
|
||||
return repositories.find((repo: HelmRepo) => {
|
||||
return repo.name == name
|
||||
})
|
||||
return repositories.find(repo => repo.name == name);
|
||||
}
|
||||
|
||||
public async update() {
|
||||
const helm = await helmCli.binaryPath()
|
||||
logger.debug(`${helm} repo update`)
|
||||
|
||||
const { stdout } = await promiseExec(`"${helm}" repo update`).catch((error) => {
|
||||
return { stdout: error.stdout }
|
||||
})
|
||||
return stdout
|
||||
}
|
||||
|
||||
public async addRepo(repository: HelmRepo) {
|
||||
public async addRepo({ name, url }: HelmRepo) {
|
||||
logger.info(`[HELM]: adding repo "${name}" from ${url}`);
|
||||
const helm = await helmCli.binaryPath()
|
||||
logger.debug(`${helm} repo add ${repository.name} ${repository.url}`)
|
||||
const { stdout } = await promiseExec(`"${helm}" repo add ${name} ${url}`).catch((error) => {
|
||||
throw(error.stderr)
|
||||
})
|
||||
return stdout
|
||||
}
|
||||
|
||||
const { stdout } = await promiseExec(`"${helm}" repo add ${repository.name} ${repository.url}`).catch((error) => {
|
||||
public async removeRepo({ name, url }: HelmRepo): Promise<string> {
|
||||
logger.info(`[HELM]: removing repo "${name}" from ${url}`);
|
||||
const helm = await helmCli.binaryPath()
|
||||
const { stdout, stderr } = await promiseExec(`"${helm}" repo remove ${name}`).catch((error) => {
|
||||
throw(error.stderr)
|
||||
})
|
||||
return stdout
|
||||
|
||||
+35
-37
@@ -1,15 +1,15 @@
|
||||
import { app, remote } from "electron"
|
||||
import path from "path"
|
||||
import fs from "fs"
|
||||
import request from "request"
|
||||
import { promiseExec} from "./promise-exec"
|
||||
import { promiseExec } from "./promise-exec"
|
||||
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/helm-cli"
|
||||
import { userStore } from "../common/user-store"
|
||||
import { getBundledKubectlVersion} from "../common/utils/app-version"
|
||||
import { customRequest } from "../common/request";
|
||||
import { getBundledKubectlVersion } from "../common/utils/app-version"
|
||||
import { isDevelopment, isWindows } from "../common/vars";
|
||||
|
||||
const bundledVersion = getBundledKubectlVersion()
|
||||
const kubectlMap: Map<string, string> = new Map([
|
||||
@@ -32,18 +32,18 @@ const packageMirrors: Map<string, string> = new Map([
|
||||
["china", "https://mirror.azure.cn/kubernetes/kubectl"]
|
||||
])
|
||||
|
||||
let bundledPath: string
|
||||
const initScriptVersionString = "# lens-initscript v3\n"
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV !== "production"
|
||||
let bundledPath: string = null
|
||||
|
||||
if(isDevelopment) {
|
||||
if (isDevelopment) {
|
||||
bundledPath = path.join(process.cwd(), "binaries", "client", process.platform, process.arch, "kubectl")
|
||||
} else {
|
||||
bundledPath = path.join(process.resourcesPath, process.arch, "kubectl")
|
||||
}
|
||||
|
||||
if(process.platform === "win32") bundledPath = `${bundledPath}.exe`
|
||||
if (isWindows) {
|
||||
bundledPath = `${bundledPath}.exe`
|
||||
}
|
||||
|
||||
export class Kubectl {
|
||||
public kubectlVersion: string
|
||||
@@ -52,7 +52,7 @@ export class Kubectl {
|
||||
protected path: string
|
||||
protected dirname: string
|
||||
|
||||
static get kubectlDir(){
|
||||
static get kubectlDir() {
|
||||
return path.join((app || remote.app).getPath("userData"), "binaries", "kubectl")
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ export class Kubectl {
|
||||
|
||||
// Returns the single bundled Kubectl instance
|
||||
public static bundled() {
|
||||
if(!Kubectl.bundledInstance) Kubectl.bundledInstance = new Kubectl(Kubectl.bundledKubectlVersion)
|
||||
if (!Kubectl.bundledInstance) Kubectl.bundledInstance = new Kubectl(Kubectl.bundledKubectlVersion)
|
||||
return Kubectl.bundledInstance
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ export class Kubectl {
|
||||
const minorVersion = versionParts[1]
|
||||
/* minorVersion is the first two digits of kube server version
|
||||
if the version map includes that, use that version, if not, fallback to the exact x.y.z of kube version */
|
||||
if(kubectlMap.has(minorVersion)) {
|
||||
if (kubectlMap.has(minorVersion)) {
|
||||
this.kubectlVersion = kubectlMap.get(minorVersion)
|
||||
logger.debug("Set kubectl version " + this.kubectlVersion + " for cluster version " + clusterVersion + " using version map")
|
||||
} else {
|
||||
@@ -81,16 +81,16 @@ export class Kubectl {
|
||||
|
||||
let arch = null
|
||||
|
||||
if(process.arch == "x64") {
|
||||
if (process.arch == "x64") {
|
||||
arch = "amd64"
|
||||
} else if(process.arch == "x86" || process.arch == "ia32") {
|
||||
} else if (process.arch == "x86" || process.arch == "ia32") {
|
||||
arch = "386"
|
||||
} else {
|
||||
arch = process.arch
|
||||
}
|
||||
|
||||
const platformName = process.platform === "win32" ? "windows" : process.platform
|
||||
const binaryName = process.platform === "win32" ? "kubectl.exe" : "kubectl"
|
||||
const platformName = isWindows ? "windows" : process.platform
|
||||
const binaryName = isWindows ? "kubectl.exe" : "kubectl"
|
||||
|
||||
this.url = `${this.getDownloadMirror()}/v${this.kubectlVersion}/bin/${platformName}/${arch}/${binaryName}`
|
||||
|
||||
@@ -102,7 +102,7 @@ export class Kubectl {
|
||||
try {
|
||||
await this.ensureKubectl()
|
||||
return this.path
|
||||
} catch(err) {
|
||||
} catch (err) {
|
||||
logger.error("Failed to ensure kubectl, fallback to the bundled version")
|
||||
logger.error(err)
|
||||
return Kubectl.bundledKubectlPath
|
||||
@@ -113,7 +113,7 @@ export class Kubectl {
|
||||
try {
|
||||
await this.ensureKubectl()
|
||||
return this.dirname
|
||||
} catch(err) {
|
||||
} catch (err) {
|
||||
logger.error(err)
|
||||
return ""
|
||||
}
|
||||
@@ -138,8 +138,7 @@ export class Kubectl {
|
||||
return true
|
||||
}
|
||||
logger.error(`Local kubectl is version ${version}, expected ${this.kubectlVersion}, unlinking`)
|
||||
}
|
||||
catch(err) {
|
||||
} catch (err) {
|
||||
logger.error(`Local kubectl failed to run properly (${err.message}), unlinking`)
|
||||
}
|
||||
await fs.promises.unlink(this.path)
|
||||
@@ -148,7 +147,7 @@ export class Kubectl {
|
||||
}
|
||||
|
||||
protected async checkBundled(): Promise<boolean> {
|
||||
if(this.kubectlVersion === Kubectl.bundledKubectlVersion) {
|
||||
if (this.kubectlVersion === Kubectl.bundledKubectlVersion) {
|
||||
try {
|
||||
const exist = await pathExists(this.path)
|
||||
if (!exist) {
|
||||
@@ -156,7 +155,7 @@ export class Kubectl {
|
||||
await fs.promises.chmod(this.path, 0o755)
|
||||
}
|
||||
return true
|
||||
} catch(err) {
|
||||
} catch (err) {
|
||||
logger.error("Could not copy the bundled kubectl to app-data: " + err)
|
||||
return false
|
||||
}
|
||||
@@ -171,10 +170,15 @@ export class Kubectl {
|
||||
logger.debug(`Acquired a lock for ${this.kubectlVersion}`)
|
||||
const bundled = await this.checkBundled()
|
||||
const isValid = await this.checkBinary(!bundled)
|
||||
if(!isValid) {
|
||||
await this.downloadKubectl().catch((error) => { logger.error(error) });
|
||||
if (!isValid) {
|
||||
await this.downloadKubectl().catch((error) => {
|
||||
logger.error(error)
|
||||
});
|
||||
}
|
||||
await this.writeInitScripts().catch((error) => { logger.error("Failed to write init scripts"); logger.error(error) })
|
||||
await this.writeInitScripts().catch((error) => {
|
||||
logger.error("Failed to write init scripts");
|
||||
logger.error(error)
|
||||
})
|
||||
logger.debug(`Releasing lock for ${this.kubectlVersion}`)
|
||||
release()
|
||||
return true
|
||||
@@ -190,10 +194,10 @@ export class Kubectl {
|
||||
|
||||
logger.info(`Downloading kubectl ${this.kubectlVersion} from ${this.url} to ${this.path}`)
|
||||
return new Promise((resolve, reject) => {
|
||||
const stream = request({
|
||||
const stream = customRequest({
|
||||
url: this.url,
|
||||
gzip: true,
|
||||
...this.getRequestOpts()
|
||||
})
|
||||
});
|
||||
const file = fs.createWriteStream(this.path)
|
||||
stream.on("complete", () => {
|
||||
logger.debug("kubectl binary download finished")
|
||||
@@ -215,7 +219,7 @@ export class Kubectl {
|
||||
|
||||
protected async scriptIsLatest(scriptPath: string) {
|
||||
const scriptExists = await pathExists(scriptPath)
|
||||
if(!scriptExists) return false
|
||||
if (!scriptExists) return false
|
||||
|
||||
try {
|
||||
const filehandle = await fs.promises.open(scriptPath, 'r')
|
||||
@@ -234,7 +238,7 @@ export class Kubectl {
|
||||
const fsPromises = fs.promises;
|
||||
const bashScriptPath = path.join(this.dirname, '.bash_set_path')
|
||||
const bashScriptIsLatest = await this.scriptIsLatest(bashScriptPath)
|
||||
if(!bashScriptIsLatest) {
|
||||
if (!bashScriptIsLatest) {
|
||||
let bashScript = "" + initScriptVersionString
|
||||
bashScript += "tempkubeconfig=\"$KUBECONFIG\"\n"
|
||||
bashScript += "test -f \"/etc/profile\" && . \"/etc/profile\"\n"
|
||||
@@ -253,7 +257,7 @@ export class Kubectl {
|
||||
|
||||
const zshScriptPath = path.join(this.dirname, '.zlogin')
|
||||
const zshScriptIsLatest = await this.scriptIsLatest(zshScriptPath)
|
||||
if(!zshScriptIsLatest) {
|
||||
if (!zshScriptIsLatest) {
|
||||
let zshScript = "" + initScriptVersionString
|
||||
|
||||
zshScript += "tempkubeconfig=\"$KUBECONFIG\"\n"
|
||||
@@ -280,12 +284,6 @@ export class Kubectl {
|
||||
}
|
||||
}
|
||||
|
||||
protected getRequestOpts() {
|
||||
return globalRequestOpts({
|
||||
url: this.url
|
||||
})
|
||||
}
|
||||
|
||||
protected getDownloadMirror() {
|
||||
const mirror = packageMirrors.get(userStore.preferences.downloadMirror)
|
||||
if (mirror) {
|
||||
|
||||
Reference in new issue
Block a user