From c65f8745e73e759ec78bfae17b3c622d153b121c Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 6 Oct 2021 15:06:37 -0400 Subject: [PATCH] Remove request and request-promise-native in favour of got Signed-off-by: Sebastian Malton --- build/download_kubectl.ts | 36 +++-- package.json | 4 +- src/common/request.ts | 50 ------- src/common/utils/app-version.ts | 15 +- src/common/utils/downloadFile.ts | 12 +- src/main/__test__/cluster.test.ts | 2 - .../base-cluster-detector.ts | 15 +- .../cluster-detectors/detector-registry.ts | 48 +++--- src/main/helm/helm-repo-manager.ts | 138 +++++++++--------- src/main/k8s-request.ts | 34 +++-- src/main/kubectl.ts | 15 +- src/main/lens-binary.ts | 31 ++-- .../components/+preferences/helm-charts.tsx | 13 +- yarn.lock | 55 +++---- 14 files changed, 224 insertions(+), 244 deletions(-) delete mode 100644 src/common/request.ts diff --git a/build/download_kubectl.ts b/build/download_kubectl.ts index e87534d24f00..82faa728a2a6 100644 --- a/build/download_kubectl.ts +++ b/build/download_kubectl.ts @@ -20,19 +20,18 @@ */ import packageInfo from "../package.json"; import fs from "fs"; -import request from "request"; import md5File from "md5-file"; -import requestPromise from "request-promise-native"; import { ensureDir, pathExists } from "fs-extra"; import path from "path"; import { noop } from "lodash"; import { isLinux, isMac } from "../src/common/vars"; +import got from "got"; class KubectlDownloader { - public kubectlVersion: string; - protected url: string; - protected path: string; - protected dirname: string; + public readonly kubectlVersion: string; + protected readonly url: string; + protected readonly path: string; + protected readonly dirname: string; constructor(clusterVersion: string, platform: string, arch: string, target: string) { this.kubectlVersion = clusterVersion; @@ -44,14 +43,14 @@ class KubectlDownloader { } protected async urlEtag() { - const response = await requestPromise({ - method: "HEAD", - uri: this.url, - resolveWithFullResponse: true - }).catch(console.error); - - if (response.headers["etag"]) { - return response.headers["etag"].replace(/"/g, ""); + try { + const response = await got.head(this.url); + + if (response.headers["etag"]) { + return response.headers["etag"].replace(/"/g, ""); + } + } catch (error) { + console.error(error); } return ""; @@ -87,11 +86,10 @@ class KubectlDownloader { const file = fs.createWriteStream(this.path); console.log(`Downloading kubectl ${this.kubectlVersion} from ${this.url} to ${this.path}`); - const requestOpts: request.UriOptions & request.CoreOptions = { - uri: this.url, - gzip: true - }; - const stream = request(requestOpts); + const stream = got.stream({ + url: this.url, + decompress: true, + }); stream.on("complete", () => { console.log("kubectl binary download finished"); diff --git a/package.json b/package.json index a5677e015434..8f149cf40c20 100644 --- a/package.json +++ b/package.json @@ -202,9 +202,11 @@ "filehound": "^1.17.4", "fs-extra": "^9.0.1", "glob-to-regexp": "^0.4.1", + "got": "^11.8.2", "grapheme-splitter": "^1.0.4", "handlebars": "^4.7.7", "http-proxy": "^1.18.1", + "http-proxy-agent": "^5.0.0", "immer": "^8.0.4", "joi": "^17.4.2", "js-yaml": "^3.14.0", @@ -235,8 +237,6 @@ "react-router": "^5.2.0", "react-virtualized-auto-sizer": "^1.0.6", "readable-stream": "^3.6.0", - "request": "^2.88.2", - "request-promise-native": "^1.0.9", "rfc6902": "^4.0.2", "semver": "^7.3.2", "serializr": "^2.0.5", diff --git a/src/common/request.ts b/src/common/request.ts deleted file mode 100644 index a1e6651218ba..000000000000 --- a/src/common/request.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (c) 2021 OpenLens Authors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -import request from "request"; -import requestPromise from "request-promise-native"; -import { UserStore } from "./user-store"; - -// todo: get rid of "request" (deprecated) -// https://github.com/lensapp/lens/issues/459 - -function getDefaultRequestOpts(): Partial { - const { httpsProxy, allowUntrustedCAs } = UserStore.getInstance(); - - return { - proxy: httpsProxy || undefined, - rejectUnauthorized: !allowUntrustedCAs, - }; -} - -/** - * @deprecated - */ -export function customRequest(opts: request.Options) { - return request.defaults(getDefaultRequestOpts())(opts); -} - -/** - * @deprecated - */ -export function customRequestPromise(opts: requestPromise.Options) { - return requestPromise.defaults(getDefaultRequestOpts())(opts); -} diff --git a/src/common/utils/app-version.ts b/src/common/utils/app-version.ts index f05877a7ec55..8832deeee3a8 100644 --- a/src/common/utils/app-version.ts +++ b/src/common/utils/app-version.ts @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import requestPromise from "request-promise-native"; +import got from "got"; import packageInfo from "../../../package.json"; export function getAppVersion(): string { @@ -30,13 +30,14 @@ export function getBundledKubectlVersion(): string { return packageInfo.config.bundledKubectlVersion; } +interface AppVersion { + version: string; +} + export async function getAppVersionFromProxyServer(proxyPort: number): Promise { - const response = await requestPromise({ - method: "GET", - uri: `http://127.0.0.1:${proxyPort}/version`, - resolveWithFullResponse: true, - proxy: undefined, + const { body } = await got(`http://localhost:${proxyPort}/version`, { + responseType: "json", }); - return JSON.parse(response.body).version; + return body.version; } diff --git a/src/common/utils/downloadFile.ts b/src/common/utils/downloadFile.ts index 6ec2f2ad4a2b..f5f55a1f1165 100644 --- a/src/common/utils/downloadFile.ts +++ b/src/common/utils/downloadFile.ts @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import request from "request"; +import got from "got"; export interface DownloadFileOptions { url: string; @@ -35,7 +35,11 @@ export interface DownloadFileTicket { export function downloadFile({ url, timeout, gzip = true }: DownloadFileOptions): DownloadFileTicket { const fileChunks: Buffer[] = []; - const req = request(url, { gzip, timeout }); + const req = got.stream({ + url, + timeout, + decompress: gzip, + }); const promise: Promise = new Promise((resolve, reject) => { req.on("data", (chunk: Buffer) => { fileChunks.push(chunk); @@ -52,8 +56,8 @@ export function downloadFile({ url, timeout, gzip = true }: DownloadFileOptions) url, promise, cancel() { - req.abort(); - } + req.destroy(); + }, }; } diff --git a/src/main/__test__/cluster.test.ts b/src/main/__test__/cluster.test.ts index 8c50518c8564..634b07e59945 100644 --- a/src/main/__test__/cluster.test.ts +++ b/src/main/__test__/cluster.test.ts @@ -47,7 +47,6 @@ jest.mock("winston", () => ({ jest.mock("../../common/ipc"); jest.mock("../context-handler"); jest.mock("request"); -jest.mock("request-promise-native"); import { Console } from "console"; import mockFs from "mock-fs"; @@ -114,7 +113,6 @@ describe("create clusters", () => { }); it("activating cluster should try to connect to cluster and do a refresh", async () => { - const c = new class extends Cluster { // only way to mock protected methods, without these we leak promises protected bindEvents() { diff --git a/src/main/cluster-detectors/base-cluster-detector.ts b/src/main/cluster-detectors/base-cluster-detector.ts index 1edd971cfd3d..ce270a4c3133 100644 --- a/src/main/cluster-detectors/base-cluster-detector.ts +++ b/src/main/cluster-detectors/base-cluster-detector.ts @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import type { RequestPromiseOptions } from "request-promise-native"; +import type { OptionsOfJSONResponseBody } from "got"; import type { Cluster } from "../cluster"; import { k8sRequest } from "../k8s-request"; @@ -28,17 +28,14 @@ export type ClusterDetectionResult = { accuracy: number }; -export class BaseClusterDetector { - key: string; +export abstract class BaseClusterDetector { + abstract key: string; - constructor(public cluster: Cluster) { - } + constructor(public cluster: Cluster) {} - detect(): Promise { - return null; - } + abstract detect(): Promise; - protected async k8sRequest(path: string, options: RequestPromiseOptions = {}): Promise { + protected async k8sRequest(path: string, options: OptionsOfJSONResponseBody = {}): Promise { return k8sRequest(this.cluster, path, options); } } diff --git a/src/main/cluster-detectors/detector-registry.ts b/src/main/cluster-detectors/detector-registry.ts index 5d3461b0bbc1..a079e53a84f5 100644 --- a/src/main/cluster-detectors/detector-registry.ts +++ b/src/main/cluster-detectors/detector-registry.ts @@ -21,43 +21,49 @@ import { observable } from "mobx"; import type { ClusterMetadata } from "../../common/cluster-types"; -import { Singleton } from "../../common/utils"; +import { iter, Singleton } from "../../common/utils"; import type { Cluster } from "../cluster"; import type { BaseClusterDetector, ClusterDetectionResult } from "./base-cluster-detector"; +export type ClusterDetectionConstructor = new (cluster: Cluster) => BaseClusterDetector; + export class DetectorRegistry extends Singleton { - registry = observable.array([], { deep: false }); + private registry = observable.array([], { deep: false }); - add(detectorClass: typeof BaseClusterDetector): this { + add(detectorClass: ClusterDetectionConstructor): this { this.registry.push(detectorClass); return this; } async detectForCluster(cluster: Cluster): Promise { - const results: {[key: string]: ClusterDetectionResult } = {}; + const results = new Map(); + const detections = this.registry.map(async DetectorClass => { + const detector = new DetectorClass(cluster); - for (const detectorClass of this.registry) { - const detector = new detectorClass(cluster); + return [detector.key, await detector.detect()] as const; + }); + for (const detection of detections) { try { - const data = await detector.detect(); - - if (!data) continue; - const existingValue = results[detector.key]; - - if (existingValue && existingValue.accuracy > data.accuracy) continue; // previous value exists and is more accurate - results[detector.key] = data; - } catch (e) { - // detector raised error, do nothing - } - } - const metadata: ClusterMetadata = {}; + const [key, data] = await detection; - for (const [key, result] of Object.entries(results)) { - metadata[key] = result.value; + if ( + data && ( + !results.has(key) + || results.get(key).accuracy <= data.accuracy + ) + ) { + results.set(key, data); + } + } catch {} // ignore errors } - return metadata; + return Object.fromEntries( + iter.map( + Object.entries(results), + ([key, { value }]) => [key, value] + ) + ); } } diff --git a/src/main/helm/helm-repo-manager.ts b/src/main/helm/helm-repo-manager.ts index 9e4038a10f7f..eddb3bbcaf11 100644 --- a/src/main/helm/helm-repo-manager.ts +++ b/src/main/helm/helm-repo-manager.ts @@ -21,12 +21,11 @@ import yaml from "js-yaml"; import { readFile } from "fs-extra"; -import { promiseExec } from "../promise-exec"; +import { promiseExecFile } 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"; +import { iter } from "../../common/utils"; export type HelmEnv = Record & { HELM_REPOSITORY_CACHE?: string; @@ -49,50 +48,49 @@ export interface HelmRepo { password?: string, } +/** + * A Regex for matching strings of the form = where key MUST NOT + * include a '=' + */ +const envVarMatcher = /^((?[^=]*)=(?.*))$/; + export class HelmRepoManager extends Singleton { - protected repos: HelmRepo[]; - protected helmEnv: HelmEnv; - protected initialized: boolean; - - public static async loadAvailableRepos(): Promise { - const res = await customRequestPromise({ - uri: "https://github.com/lensapp/artifact-hub-repositories/releases/download/latest/repositories.json", - json: true, - resolveWithFullResponse: true, - timeout: 10000, - }); - - return orderBy(res.body, repo => repo.name); - } + protected helmEnv: HelmEnv | null = null; - private async init() { + private async ensureInitialized() { helmCli.setLogger(logger); await helmCli.ensureBinary(); - if (!this.initialized) { + if (!this.helmEnv) { this.helmEnv = await HelmRepoManager.parseHelmEnv(); await HelmRepoManager.update(); - this.initialized = true; } } - protected static async parseHelmEnv() { + protected static async executeHelm(args: string[]): Promise { 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("="); + try { + const { stdout } = await promiseExecFile(helm, args); - if (key && value) { - env[key] = value.replace(/"/g, ""); // strip quotas - } - }); + return stdout; + } catch (error) { + throw error?.stderr || error; + } + } - return env; + protected static async parseHelmEnv(): Promise> { + const output = await HelmRepoManager.executeHelm(["env"]); + + return Object.fromEntries( + iter.map( + iter.filterMap( + output.split(/\r?\n/), + line => line.match(envVarMatcher), + ), + ({ groups: { key, value }}) => [key, JSON.parse(value)] + ) + ); } public async repo(name: string): Promise { @@ -103,9 +101,7 @@ export class HelmRepoManager extends Singleton { public async repositories(): Promise { try { - if (!this.initialized) { - await this.init(); - } + await this.ensureInitialized(); const repoConfigFile = this.helmEnv.HELM_REPOSITORY_CONFIG; const { repositories }: HelmRepoConfig = await readFile(repoConfigFile, "utf8") @@ -132,50 +128,54 @@ export class HelmRepoManager extends Singleton { } public static async update() { - const helm = await helmCli.binaryPath(); - const { stdout } = await promiseExec(`"${helm}" repo update`).catch((error) => { - return { stdout: error.stdout }; - }); + return HelmRepoManager.executeHelm(["repo", "update"]); + } - return stdout; + public static async addRepo({ name, url }: HelmRepo): Promise { + logger.info(`[HELM]: adding repo "${name}" from ${url}`); + + await HelmRepoManager.executeHelm(["repo", "add", name, url]); } - public static async addRepo({ name, url }: HelmRepo) { + public static async addCustomRepo(repoAttributes: HelmRepo): Promise { + const { + name, url, insecureSkipTlsVerify, caFile, certFile, + keyFile, password, username, + } = repoAttributes; + 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); - }); + const args = ["repo", "add", name, url]; - return stdout; - } + if (insecureSkipTlsVerify) { + args.push("--insecure-skip-tls-verify"); + } - public static async addCustomRepo(repoAttributes : HelmRepo) { - logger.info(`[HELM]: adding repo "${repoAttributes.name}" from ${repoAttributes.url}`); - const helm = await helmCli.binaryPath(); + if (username) { + args.push("--username", username); + } - const insecureSkipTlsVerify = repoAttributes.insecureSkipTlsVerify ? " --insecure-skip-tls-verify" : ""; - const username = repoAttributes.username ? ` --username "${repoAttributes.username}"` : ""; - const password = repoAttributes.password ? ` --password "${repoAttributes.password}"` : ""; - const caFile = repoAttributes.caFile ? ` --ca-file "${repoAttributes.caFile}"` : ""; - const keyFile = repoAttributes.keyFile ? ` --key-file "${repoAttributes.keyFile}"` : ""; - const certFile = repoAttributes.certFile ? ` --cert-file "${repoAttributes.certFile}"` : ""; + if (password) { + args.push("--password", password); + } - const addRepoCommand = `"${helm}" repo add ${repoAttributes.name} ${repoAttributes.url}${insecureSkipTlsVerify}${username}${password}${caFile}${keyFile}${certFile}`; - const { stdout } = await promiseExec(addRepoCommand).catch((error) => { - throw(error.stderr); - }); + if (caFile) { + args.push("--ca-file", caFile); + } - return stdout; + if (keyFile) { + args.push("--key-file", keyFile); + } + + if (certFile) { + args.push("--cert-file", certFile); + } + + await HelmRepoManager.executeHelm(args); } - public static async removeRepo({ name, url }: HelmRepo): Promise { + public static async removeRepo({ name, url }: HelmRepo): Promise { logger.info(`[HELM]: removing repo "${name}" from ${url}`); - const helm = await helmCli.binaryPath(); - const { stdout } = await promiseExec(`"${helm}" repo remove ${name}`).catch((error) => { - throw(error.stderr); - }); - - return stdout; + + await HelmRepoManager.executeHelm(["repo", "remove", name]); } } diff --git a/src/main/k8s-request.ts b/src/main/k8s-request.ts index 923ec816094d..7dac620c3d64 100644 --- a/src/main/k8s-request.ts +++ b/src/main/k8s-request.ts @@ -19,32 +19,40 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import request, { RequestPromiseOptions } from "request-promise-native"; import { apiKubePrefix } from "../common/vars"; import type { IMetricsReqParams } from "../common/k8s-api/endpoints/metrics.api"; import { LensProxy } from "./lens-proxy"; import type { Cluster } from "./cluster"; +import got, { OptionsOfJSONResponseBody } from "got"; -export async function k8sRequest(cluster: Cluster, path: string, options: RequestPromiseOptions = {}): Promise { - const kubeProxyUrl = `http://localhost:${LensProxy.getInstance().port}${apiKubePrefix}`; +function getKubeProxyUrl() { + return `http://localhost:${LensProxy.getInstance().port}${apiKubePrefix}`; +} + +export async function k8sRequest(cluster: Cluster, path: string, options: OptionsOfJSONResponseBody = {}): Promise { + const kubeProxyUrl = getKubeProxyUrl(); - options.headers ??= {}; - options.json ??= true; options.timeout ??= 30000; + options.headers ??= {}; options.headers.Host = `${cluster.id}.${new URL(kubeProxyUrl).host}`; // required in ClusterManager.getClusterForRequest() + options.responseType = "json"; - return request(kubeProxyUrl + path, options); + const { body } = await got(kubeProxyUrl + path, options); + + return body; } export async function getMetrics(cluster: Cluster, prometheusPath: string, queryParams: IMetricsReqParams & { query: string }): Promise { const prometheusPrefix = cluster.preferences.prometheus?.prefix || ""; - const metricsPath = `/api/v1/namespaces/${prometheusPath}/proxy${prometheusPrefix}/api/v1/query_range`; - - return k8sRequest(cluster, metricsPath, { - timeout: 0, - resolveWithFullResponse: false, - json: true, - method: "POST", + const kubeProxyUrl = getKubeProxyUrl(); + const url = `${kubeProxyUrl}/api/v1/namespaces/${prometheusPath}/proxy${prometheusPrefix}/api/v1/query_range`; + const { body } = await got.post(url, { form: queryParams, + headers: { + Host: `${cluster.id}.${new URL(kubeProxyUrl).host}`, + }, + responseType: "json" }); + + return body; } diff --git a/src/main/kubectl.ts b/src/main/kubectl.ts index 4695e8272eaa..e09fac70aebc 100644 --- a/src/main/kubectl.ts +++ b/src/main/kubectl.ts @@ -27,11 +27,12 @@ import { ensureDir, pathExists } from "fs-extra"; import * as lockFile from "proper-lockfile"; import { helmCli } from "./helm/helm-cli"; import { UserStore } from "../common/user-store"; -import { customRequest } from "../common/request"; import { getBundledKubectlVersion } from "../common/utils/app-version"; import { isDevelopment, isWindows, isTestEnv } from "../common/vars"; import { SemVer } from "semver"; import { getPath } from "../common/utils/getPath"; +import got, { Agents } from "got"; +import { HttpProxyAgent } from "http-proxy-agent"; const bundledVersion = getBundledKubectlVersion(); const kubectlMap: Map = new Map([ @@ -302,9 +303,17 @@ export class Kubectl { logger.info(`Downloading kubectl ${this.kubectlVersion} from ${this.url} to ${this.path}`); return new Promise((resolve, reject) => { - const stream = customRequest({ + const { httpsProxy, allowUntrustedCAs } = UserStore.getInstance(); + const agent: Agents | false = httpsProxy + ? { + https: new HttpProxyAgent(httpsProxy) + } + : false; + const stream = got.stream({ url: this.url, - gzip: true, + decompress: true, + rejectUnauthorized: !allowUntrustedCAs, + agent, }); const file = fs.createWriteStream(this.path); diff --git a/src/main/lens-binary.ts b/src/main/lens-binary.ts index 2c52b05db5f3..c18e5752e6ff 100644 --- a/src/main/lens-binary.ts +++ b/src/main/lens-binary.ts @@ -21,18 +21,23 @@ import path from "path"; import fs from "fs"; -import request from "request"; import { ensureDir, pathExists } from "fs-extra"; import * as tar from "tar"; import { isWindows } from "../common/vars"; import type winston from "winston"; +import got, { Options } from "got"; +import type { Merge } from "type-fest"; + +type GotStreamOptions = Merge; export type LensBinaryOpts = { version: string; baseDir: string; originalBinaryName: string; newBinaryName?: string; - requestOpts?: request.Options; + requestOpts?: GotStreamOptions; }; export class LensBinary { @@ -47,7 +52,7 @@ export class LensBinary { protected platformName: string; protected arch: string; protected originalBinaryName: string; - protected requestOpts: request.Options; + protected requestOpts: GotStreamOptions; protected logger: Console | winston.Logger; constructor(opts: LensBinaryOpts) { @@ -56,7 +61,7 @@ export class LensBinary { this.originalBinaryName = opts.originalBinaryName; this.binaryName = opts.newBinaryName || opts.originalBinaryName; this.binaryVersion = opts.version; - this.requestOpts = opts.requestOpts; + this.requestOpts = opts.requestOpts || {}; this.logger = console; let arch = null; @@ -176,20 +181,20 @@ export class LensBinary { } protected async downloadBinary() { + const binaryPath = this.tarPath || this.getBinaryPath(); - + await ensureDir(this.getBinaryDir(), 0o755); - + const file = fs.createWriteStream(binaryPath); const url = this.getUrl(); - + this.logger.info(`Downloading ${this.originalBinaryName} ${this.binaryVersion} from ${url} to ${binaryPath}`); - const requestOpts: request.UriOptions & request.CoreOptions = { - uri: url, - gzip: true, - ...this.requestOpts - }; - const stream = request(requestOpts); + + const stream = got.stream(url, { + decompress: true, + ...this.requestOpts, + }); stream.on("complete", () => { this.logger.info(`Download of ${this.originalBinaryName} finished`); diff --git a/src/renderer/components/+preferences/helm-charts.tsx b/src/renderer/components/+preferences/helm-charts.tsx index d0dd47e464bd..0fddc102ba0e 100644 --- a/src/renderer/components/+preferences/helm-charts.tsx +++ b/src/renderer/components/+preferences/helm-charts.tsx @@ -34,6 +34,17 @@ import { observer } from "mobx-react"; import { RemovableItem } from "./removable-item"; import { Notice } from "../+extensions/notice"; import { Spinner } from "../spinner"; +import got from "got"; +import { orderBy } from "lodash"; + +async function loadAvailableHelmRepos(): Promise { + const { body } = await got.get("https://github.com/lensapp/artifact-hub-repositories/releases/download/latest/repositories.json", { + timeout: 10_000, + responseType: "json" + }); + + return orderBy(body, repo => repo.name); +} @observer export class HelmCharts extends React.Component { @@ -63,7 +74,7 @@ export class HelmCharts extends React.Component { try { if (!this.repos.length) { - this.repos = await HelmRepoManager.loadAvailableRepos(); + this.repos = await loadAvailableHelmRepos(); } const repos = await HelmRepoManager.getInstance().repositories(); // via helm-cli diff --git a/yarn.lock b/yarn.lock index be0a20679233..056cb50c4b77 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1269,6 +1269,11 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + "@tsconfig/node10@^1.0.7": version "1.0.8" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" @@ -6708,7 +6713,7 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -got@^11.8.0: +got@^11.8.0, got@^11.8.2: version "11.8.2" resolved "https://registry.yarnpkg.com/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599" integrity sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ== @@ -7097,6 +7102,15 @@ http-proxy-agent@^4.0.1: agent-base "6" debug "4" +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + http-proxy-middleware@0.19.1: version "0.19.1" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" @@ -9169,7 +9183,7 @@ lodash.without@~4.4.0: resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac" integrity sha1-PNRXSgC2e643OpS3SHcmQFB7eqw= -lodash@4.x, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: +lodash@4.x, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -12057,22 +12071,6 @@ repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -request-promise-core@1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" - integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== - dependencies: - lodash "^4.17.19" - -request-promise-native@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" - integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== - dependencies: - request-promise-core "1.1.4" - stealthy-require "^1.1.1" - tough-cookie "^2.3.3" - request@^2.88.0, request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" @@ -13020,11 +13018,6 @@ static-extend@^0.1.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= -stealthy-require@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" - integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= - stream-browserify@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" @@ -13703,14 +13696,6 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -tough-cookie@^2.3.3, tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - tough-cookie@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" @@ -13720,6 +13705,14 @@ tough-cookie@^4.0.0: punycode "^2.1.1" universalify "^0.1.2" +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + tr46@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240"