mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Migrating Vue components to React and stores refactoring (#585)
Signed-off-by: Roman <ixrock@gmail.com> Co-authored-by: Sebastian Malton <sebastian@malton.name> Co-authored-by: Sebastian Malton <smalton@mirantis.com> Co-authored-by: Lauri Nevala <lauri.nevala@gmail.com> Co-authored-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
297 files changed
+8186
-9103
No files matched your search
@@ -0,0 +1,74 @@
|
||||
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 { helmCli } from "./helm-cli"
|
||||
|
||||
type CachedYaml = {
|
||||
entries: any; // todo: types
|
||||
}
|
||||
|
||||
export class HelmChartManager {
|
||||
protected cache: any = {}
|
||||
protected repo: HelmRepo
|
||||
|
||||
constructor(repo: HelmRepo){
|
||||
this.cache = HelmRepoManager.cache
|
||||
this.repo = repo
|
||||
}
|
||||
|
||||
public async chart(name: string) {
|
||||
const charts = await this.charts()
|
||||
return charts[name]
|
||||
}
|
||||
|
||||
public async charts(): Promise<any> {
|
||||
try {
|
||||
const cachedYaml = await this.cachedYaml()
|
||||
return cachedYaml["entries"]
|
||||
} catch(error) {
|
||||
logger.error(error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
public async getReadme(name: string, version = "") {
|
||||
const helm = await helmCli.binaryPath()
|
||||
if(version && version != "") {
|
||||
const { stdout, stderr} = await promiseExec(`"${helm}" show readme ${this.repo.name}/${name} --version ${version}`).catch((error) => { throw(error.stderr)})
|
||||
return stdout
|
||||
} else {
|
||||
const { stdout, stderr} = await promiseExec(`"${helm}" show readme ${this.repo.name}/${name}`).catch((error) => { throw(error.stderr)})
|
||||
return stdout
|
||||
}
|
||||
}
|
||||
|
||||
public async getValues(name: string, version = "") {
|
||||
const helm = await helmCli.binaryPath()
|
||||
if(version && version != "") {
|
||||
const { stdout, stderr} = await promiseExec(`"${helm}" show values ${this.repo.name}/${name} --version ${version}`).catch((error) => { throw(error.stderr)})
|
||||
|
||||
return stdout
|
||||
} else {
|
||||
const { stdout, stderr} = await promiseExec(`"${helm}" show values ${this.repo.name}/${name}`).catch((error) => { throw(error.stderr)})
|
||||
|
||||
return stdout
|
||||
}
|
||||
}
|
||||
|
||||
protected async cachedYaml(): Promise<CachedYaml> {
|
||||
if (!(this.repo.name in this.cache)) {
|
||||
const cacheFile = await fs.promises.readFile(this.repo.cacheFilePath, 'utf-8')
|
||||
const data = yaml.safeLoad(cacheFile)
|
||||
for(const key in data["entries"]) {
|
||||
data["entries"][key].forEach((version: any) => {
|
||||
version['repo'] = this.repo.name
|
||||
version['created'] = Date.parse(version.created).toString()
|
||||
})
|
||||
}
|
||||
this.cache[this.repo.name] = Buffer.from(JSON.stringify(data))
|
||||
}
|
||||
return JSON.parse(this.cache[this.repo.name].toString())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import packageInfo from "../../../package.json"
|
||||
import path from "path"
|
||||
import { LensBinary, LensBinaryOpts } from "../lens-binary"
|
||||
import { isProduction } from "../../common/vars";
|
||||
|
||||
export class HelmCli extends LensBinary {
|
||||
|
||||
public constructor(baseDir: string, version: string) {
|
||||
const opts: LensBinaryOpts = {
|
||||
version,
|
||||
baseDir: baseDir,
|
||||
originalBinaryName: "helm",
|
||||
newBinaryName: "helm3"
|
||||
}
|
||||
super(opts)
|
||||
}
|
||||
|
||||
protected getTarName(): string | null {
|
||||
return `${this.binaryName}-v${this.binaryVersion}-${this.platformName}-${this.arch}.tar.gz`
|
||||
}
|
||||
|
||||
protected getUrl() {
|
||||
return `https://get.helm.sh/helm-v${this.binaryVersion}-${this.platformName}-${this.arch}.tar.gz`
|
||||
}
|
||||
|
||||
protected getBinaryPath() {
|
||||
return path.join(this.dirname, this.binaryName)
|
||||
}
|
||||
|
||||
protected getOriginalBinaryPath() {
|
||||
return path.join(this.dirname, this.platformName + "-" + this.arch, this.originalBinaryName)
|
||||
}
|
||||
}
|
||||
|
||||
const helmVersion = packageInfo.config.bundledHelmVersion;
|
||||
let baseDir = process.resourcesPath;
|
||||
|
||||
if (!isProduction) {
|
||||
baseDir = path.join(process.cwd(), "binaries", "client");
|
||||
}
|
||||
|
||||
export const helmCli = new HelmCli(baseDir, helmVersion);
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import * as tempy from "tempy";
|
||||
import fs from "fs";
|
||||
import * as yaml from "js-yaml";
|
||||
import { promiseExec} from "../promise-exec"
|
||||
import { helmCli } from "./helm-cli";
|
||||
import { Cluster } from "../cluster";
|
||||
import { toCamelCase } from "../../common/utils/camelCase";
|
||||
|
||||
export class HelmReleaseManager {
|
||||
|
||||
public async listReleases(pathToKubeconfig: string, namespace?: string) {
|
||||
const helm = await helmCli.binaryPath()
|
||||
const namespaceFlag = namespace ? `-n ${namespace}` : "--all-namespaces"
|
||||
const { stdout } = await promiseExec(`"${helm}" ls --output json ${namespaceFlag} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr)})
|
||||
|
||||
const output = JSON.parse(stdout)
|
||||
if (output.length == 0) {
|
||||
return output
|
||||
}
|
||||
output.forEach((release: any, index: number) => {
|
||||
output[index] = toCamelCase(release)
|
||||
});
|
||||
return output
|
||||
}
|
||||
|
||||
|
||||
public async installChart(chart: string, values: any, name: string, namespace: string, version: string, pathToKubeconfig: string){
|
||||
const helm = await helmCli.binaryPath()
|
||||
const fileName = tempy.file({name: "values.yaml"})
|
||||
await fs.promises.writeFile(fileName, yaml.safeDump(values))
|
||||
try {
|
||||
let generateName = ""
|
||||
if (!name) {
|
||||
generateName = "--generate-name"
|
||||
name = ""
|
||||
}
|
||||
const { stdout, stderr } = await promiseExec(`"${helm}" install ${name} ${chart} --version ${version} -f ${fileName} --namespace ${namespace} --kubeconfig ${pathToKubeconfig} ${generateName}`).catch((error) => { throw(error.stderr)})
|
||||
const releaseName = stdout.split("\n")[0].split(' ')[1].trim()
|
||||
return {
|
||||
log: stdout,
|
||||
release: {
|
||||
name: releaseName,
|
||||
namespace: namespace
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
await fs.promises.unlink(fileName)
|
||||
}
|
||||
}
|
||||
|
||||
public async upgradeRelease(name: string, chart: string, values: any, namespace: string, version: string, cluster: Cluster){
|
||||
const helm = await helmCli.binaryPath()
|
||||
const fileName = tempy.file({name: "values.yaml"})
|
||||
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.getProxyKubeconfigPath()}`).catch((error) => { throw(error.stderr)})
|
||||
return {
|
||||
log: stdout,
|
||||
release: this.getRelease(name, namespace, cluster)
|
||||
}
|
||||
} finally {
|
||||
await fs.promises.unlink(fileName)
|
||||
}
|
||||
}
|
||||
|
||||
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.getProxyKubeconfigPath()}`).catch((error) => { throw(error.stderr)})
|
||||
const release = JSON.parse(stdout)
|
||||
release.resources = await this.getResources(name, namespace, cluster)
|
||||
return release
|
||||
}
|
||||
|
||||
public async deleteRelease(name: string, namespace: string, pathToKubeconfig: string) {
|
||||
const helm = await helmCli.binaryPath()
|
||||
const { stdout, stderr } = await promiseExec(`"${helm}" delete ${name} --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr)})
|
||||
|
||||
return stdout
|
||||
}
|
||||
|
||||
public async getValues(name: string, namespace: string, pathToKubeconfig: string) {
|
||||
const helm = await helmCli.binaryPath()
|
||||
const { stdout, stderr } = await promiseExec(`"${helm}" get values ${name} --all --output yaml --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr)})
|
||||
return stdout
|
||||
}
|
||||
|
||||
public async getHistory(name: string, namespace: string, pathToKubeconfig: string) {
|
||||
const helm = await helmCli.binaryPath()
|
||||
const {stdout, stderr} = await promiseExec(`"${helm}" history ${name} --output json --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr)})
|
||||
return JSON.parse(stdout)
|
||||
}
|
||||
|
||||
public async rollback(name: string, namespace: string, revision: number, pathToKubeconfig: string) {
|
||||
const helm = await helmCli.binaryPath()
|
||||
const {stdout, stderr} = await promiseExec(`"${helm}" rollback ${name} ${revision} --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr)})
|
||||
return stdout
|
||||
}
|
||||
|
||||
protected async getResources(name: string, namespace: string, cluster: Cluster) {
|
||||
const helm = await helmCli.binaryPath()
|
||||
const kubectl = await cluster.kubeCtl.getPath()
|
||||
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: []})}
|
||||
})
|
||||
return stdout
|
||||
}
|
||||
}
|
||||
|
||||
export const releaseManager = new HelmReleaseManager()
|
||||
@@ -0,0 +1,130 @@
|
||||
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 interface HelmRepoConfig {
|
||||
repositories: HelmRepo[]
|
||||
}
|
||||
|
||||
export interface HelmRepo {
|
||||
name: string;
|
||||
url: 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 = {} // todo: remove implicit updates in helm-chart-manager.ts
|
||||
|
||||
protected repos: HelmRepo[];
|
||||
protected helmEnv: HelmEnv
|
||||
protected initialized: boolean
|
||||
|
||||
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()
|
||||
await this.update()
|
||||
this.initialized = true
|
||||
}
|
||||
}
|
||||
|
||||
protected async parseHelmEnv() {
|
||||
const helm = await helmCli.binaryPath()
|
||||
const { stdout } = await promiseExec(`"${helm}" env`).catch((error) => {
|
||||
throw(error.stderr)
|
||||
})
|
||||
const lines = stdout.split(/\r?\n/) // split by new line feed
|
||||
const env: HelmEnv = {}
|
||||
lines.forEach((line: string) => {
|
||||
const [key, value] = line.split("=")
|
||||
if (key && value) {
|
||||
env[key] = value.replace(/"/g, "") // strip quotas
|
||||
}
|
||||
})
|
||||
return env
|
||||
}
|
||||
|
||||
public async repositories(): Promise<HelmRepo[]> {
|
||||
if (!this.initialized) {
|
||||
await this.init()
|
||||
}
|
||||
try {
|
||||
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 repositories.map(repo => ({
|
||||
...repo,
|
||||
cacheFilePath: `${this.helmEnv.HELM_REPOSITORY_CACHE}/${repo.name}-index.yaml`
|
||||
}));
|
||||
} catch (error) {
|
||||
logger.error(`[HELM]: repositories listing error "${error}"`)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
public async repository(name: string) {
|
||||
const repositories = await this.repositories()
|
||||
return repositories.find(repo => repo.name == name);
|
||||
}
|
||||
|
||||
public async update() {
|
||||
const helm = await helmCli.binaryPath()
|
||||
const { stdout } = await promiseExec(`"${helm}" repo update`).catch((error) => {
|
||||
return { stdout: error.stdout }
|
||||
})
|
||||
return stdout
|
||||
}
|
||||
|
||||
public async addRepo({ name, url }: HelmRepo) {
|
||||
logger.info(`[HELM]: adding repo "${name}" from ${url}`);
|
||||
const helm = await helmCli.binaryPath()
|
||||
const { stdout } = await promiseExec(`"${helm}" repo add ${name} ${url}`).catch((error) => {
|
||||
throw(error.stderr)
|
||||
})
|
||||
return stdout
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
export const repoManager = HelmRepoManager.getInstance<HelmRepoManager>()
|
||||
@@ -0,0 +1,98 @@
|
||||
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 }) {
|
||||
return await releaseManager.installChart(data.chart, data.values, data.name, data.namespace, data.version, cluster.getProxyKubeconfigPath())
|
||||
}
|
||||
|
||||
public async listCharts() {
|
||||
const charts: any = {}
|
||||
await repoManager.init()
|
||||
const repositories = await repoManager.repositories()
|
||||
for (const repo of repositories) {
|
||||
charts[repo.name] = {}
|
||||
const manager = new HelmChartManager(repo)
|
||||
let entries = await manager.charts()
|
||||
entries = this.excludeDeprecated(entries)
|
||||
for (const key in entries) {
|
||||
entries[key] = entries[key][0]
|
||||
}
|
||||
charts[repo.name] = entries
|
||||
}
|
||||
return charts
|
||||
}
|
||||
|
||||
public async getChart(repoName: string, chartName: string, version = "") {
|
||||
const result = {
|
||||
readme: "",
|
||||
versions: {}
|
||||
}
|
||||
const repo = await repoManager.repository(repoName)
|
||||
const chartManager = new HelmChartManager(repo)
|
||||
const chart = await chartManager.chart(chartName)
|
||||
result.readme = await chartManager.getReadme(chartName, version)
|
||||
result.versions = chart
|
||||
return result
|
||||
}
|
||||
|
||||
public async getChartValues(repoName: string, chartName: string, version = "") {
|
||||
const repo = await repoManager.repository(repoName)
|
||||
const chartManager = new HelmChartManager(repo)
|
||||
return chartManager.getValues(chartName, version)
|
||||
}
|
||||
|
||||
public async listReleases(cluster: Cluster, namespace: string = null) {
|
||||
await repoManager.init()
|
||||
return await releaseManager.listReleases(cluster.getProxyKubeconfigPath(), namespace)
|
||||
}
|
||||
|
||||
public async getRelease(cluster: Cluster, releaseName: string, namespace: string) {
|
||||
logger.debug("Fetch release")
|
||||
return await releaseManager.getRelease(releaseName, namespace, cluster)
|
||||
}
|
||||
|
||||
public async getReleaseValues(cluster: Cluster, releaseName: string, namespace: string) {
|
||||
logger.debug("Fetch release values")
|
||||
return await releaseManager.getValues(releaseName, namespace, cluster.getProxyKubeconfigPath())
|
||||
}
|
||||
|
||||
public async getReleaseHistory(cluster: Cluster, releaseName: string, namespace: string) {
|
||||
logger.debug("Fetch release history")
|
||||
return await releaseManager.getHistory(releaseName, namespace, cluster.getProxyKubeconfigPath())
|
||||
}
|
||||
|
||||
public async deleteRelease(cluster: Cluster, releaseName: string, namespace: string) {
|
||||
logger.debug("Delete release")
|
||||
return await releaseManager.deleteRelease(releaseName, namespace, cluster.getProxyKubeconfigPath())
|
||||
}
|
||||
|
||||
public async updateRelease(cluster: Cluster, releaseName: string, namespace: string, data: { chart: string; values: {}; version: string }) {
|
||||
logger.debug("Upgrade 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.getProxyKubeconfigPath())
|
||||
return { message: output }
|
||||
}
|
||||
|
||||
protected excludeDeprecated(entries: any) {
|
||||
for (const key in entries) {
|
||||
entries[key] = entries[key].filter((entry: any) => {
|
||||
if (Array.isArray(entry)) {
|
||||
return entry[0]['deprecated'] != true
|
||||
}
|
||||
return entry["deprecated"] != true
|
||||
})
|
||||
}
|
||||
return entries
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const helmService = new HelmService()
|
||||
Reference in new issue
Block a user