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

remove depricated package request from our code

Signed-off-by: Sebastian Malton <smalton@mirantis.com>
This commit is contained in:
Sebastian Malton 2020-09-11 13:36:32 -04:00 committed by Sebastian Malton
parent 33c405bdcf
commit 47fbf629ea
19 changed files with 397 additions and 269 deletions

View File

@ -1,10 +1,16 @@
import packageInfo from "../package.json";
import fs from "fs"; import fs from "fs";
import request from "request"; import fse from "fs-extra";
import got from "got/dist/source";
import md5File from "md5-file"; import md5File from "md5-file";
import requestPromise from "request-promise-native";
import { ensureDir, pathExists } from "fs-extra";
import path from "path"; import path from "path";
import stream from "stream";
import util, { promisify } from "util";
import packageInfo from "../package.json";
import { GotStreamFunctionOptions } from "../src/common/got-opts";
import logger from "../src/main/logger";
const pipeline = promisify(stream.pipeline);
class KubectlDownloader { class KubectlDownloader {
public kubectlVersion: string; public kubectlVersion: string;
@ -22,21 +28,26 @@ class KubectlDownloader {
} }
protected async urlEtag() { protected async urlEtag() {
const response = await requestPromise({ try {
method: "HEAD", const res = await got.head(this.url);
uri: this.url, const { etag } = res.headers;
resolveWithFullResponse: true
}).catch((error) => { console.log(error); });
if (response.headers["etag"]) { if (Array.isArray(etag)) {
return response.headers["etag"].replace(/"/g, ""); return etag[0].replace(/"/g, "");
}
if (typeof etag === "string") {
return etag.replace(/"/g, "");
}
} catch (err) {
logger.error("Failed to get etag:", err);
} }
return ""; return "";
} }
public async checkBinary() { public async checkBinary() {
const exists = await pathExists(this.path); const exists = await fse.pathExists(this.path);
if (exists) { if (exists) {
const hash = md5File.sync(this.path); const hash = md5File.sync(this.path);
@ -55,65 +66,57 @@ class KubectlDownloader {
return false; return false;
} }
public async downloadKubectl() { public async downloadKubectl(): Promise<void> {
const exists = await this.checkBinary(); const exists = await this.checkBinary();
if(exists) { if (exists) {
console.log("Already exists and is valid"); return void console.log("Already exists and is valid");
return;
} }
await ensureDir(path.dirname(this.path), 0o755);
const file = fs.createWriteStream(this.path); await fse.ensureDir(path.dirname(this.path), 0o755);
console.log(`Downloading kubectl ${this.kubectlVersion} from ${this.url} to ${this.path}`); console.log(`Downloading kubectl ${this.kubectlVersion} from ${this.url} to ${this.path}`);
const requestOpts: request.UriOptions & request.CoreOptions = { const options: GotStreamFunctionOptions = {
uri: this.url, decompress: true,
gzip: true isStream: true,
}; };
const stream = request(requestOpts);
stream.on("complete", () => { try {
console.log("kubectl binary download finished"); // TODO: improve the UI for this to use some sort of loading bar TUI
// eslint-disable-next-line @typescript-eslint/no-empty-function await pipeline(
file.end(() => {}); got.stream(this.url, options),
}); fs.createWriteStream(this.path)
);
stream.on("error", (error) => { await fse.chmod(this.path, 0o755);
console.log(error); } catch (err) {
// eslint-disable-next-line @typescript-eslint/no-empty-function logger.error("Failed to download kubectl:", err);
fs.unlink(this.path, () => {}); }
throw(error);
});
return new Promise((resolve, reject) => {
file.on("close", () => {
console.log("kubectl binary download closed");
fs.chmod(this.path, 0o755, (err) => {
if (err) reject(err);
});
resolve();
});
stream.pipe(file);
});
} }
} }
const downloadVersion = packageInfo.config.bundledKubectlVersion; const downloadVersion = packageInfo.config.bundledKubectlVersion;
const baseDir = path.join(process.env.INIT_CWD, "binaries", "client"); const baseDir = path.join(process.env.INIT_CWD, "binaries", "client");
const downloads = [
interface DownloadTarget {
platform: string;
target: string;
arch: string;
}
const downloads: DownloadTarget[] = [
{ platform: "linux", arch: "amd64", target: path.join(baseDir, "linux", "x64", "kubectl") }, { platform: "linux", arch: "amd64", target: path.join(baseDir, "linux", "x64", "kubectl") },
{ platform: "darwin", arch: "amd64", target: path.join(baseDir, "darwin", "x64", "kubectl") }, { platform: "darwin", arch: "amd64", target: path.join(baseDir, "darwin", "x64", "kubectl") },
{ platform: "windows", arch: "amd64", target: path.join(baseDir, "windows", "x64", "kubectl.exe") }, { platform: "windows", arch: "amd64", target: path.join(baseDir, "windows", "x64", "kubectl.exe") },
{ platform: "windows", arch: "386", target: path.join(baseDir, "windows", "ia32", "kubectl.exe") } { platform: "windows", arch: "386", target: path.join(baseDir, "windows", "ia32", "kubectl.exe") }
]; ];
downloads.forEach((dlOpts) => { async function downloadOne(opts: DownloadTarget) {
console.log(dlOpts); const downloader = new KubectlDownloader(downloadVersion, opts.platform, opts.arch, opts.target);
const downloader = new KubectlDownloader(downloadVersion, dlOpts.platform, dlOpts.arch, dlOpts.target);
console.log(`Downloading: ${JSON.stringify(dlOpts)}`); console.log(`Downloading: ${util.inspect(opts, false, null, true)}`);
downloader.downloadKubectl().then(() => downloader.checkBinary().then(() => console.log("Download complete"))); await downloader.downloadKubectl();
}); await downloader.checkBinary();
console.log(`Finished downloading for ${opts.platform}/${opts.arch}`);
}
Promise.all(downloads.map(downloadOne));

View File

@ -13,7 +13,7 @@ import * as utils from "../helpers/utils";
import { listHelmRepositories } from "../helpers/utils"; import { listHelmRepositories } from "../helpers/utils";
import { fail } from "assert"; import { fail } from "assert";
jest.setTimeout(60000); jest.setTimeout(120000);
// FIXME (!): improve / simplify all css-selectors + use [data-test-id="some-id"] (already used in some tests below) // FIXME (!): improve / simplify all css-selectors + use [data-test-id="some-id"] (already used in some tests below)
describe("Lens integration tests", () => { describe("Lens integration tests", () => {

View File

@ -49,12 +49,13 @@ export function describeIf(condition: boolean) {
return condition ? describe : describe.skip; return condition ? describe : describe.skip;
} }
export function setup(): Application { export function setup(waitTimeout = 60000): Application {
return new Application({ return new Application({
path: AppPaths[process.platform], // path to electron app path: AppPaths[process.platform], // path to electron app
args: [], args: [],
startTimeout: 30000, startTimeout: 30000,
waitTimeout: 60000, waitTimeout,
chromeDriverArgs: ["remote-debugging-port=9222"],
env: { env: {
CICD: "true" CICD: "true"
} }
@ -101,7 +102,7 @@ export async function tearDown(app: Application) {
await app.stop(); await app.stop();
try { try {
process.kill(pid, "SIGKILL"); process.kill(pid, 0);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }

View File

@ -207,7 +207,9 @@
"electron-window-state": "^5.0.3", "electron-window-state": "^5.0.3",
"filenamify": "^4.1.0", "filenamify": "^4.1.0",
"fs-extra": "^9.0.1", "fs-extra": "^9.0.1",
"got": "^11.8.1",
"handlebars": "^4.7.6", "handlebars": "^4.7.6",
"hpagent": "^0.1.1",
"http-proxy": "^1.18.1", "http-proxy": "^1.18.1",
"immer": "^8.0.1", "immer": "^8.0.1",
"js-yaml": "^3.14.0", "js-yaml": "^3.14.0",

31
src/common/got-opts.ts Normal file
View File

@ -0,0 +1,31 @@
import { Options } from "got/dist/source";
import { HttpsProxyAgent } from "hpagent";
import { userStore } from "./user-store";
declare type Except<ObjectType, KeysType extends keyof ObjectType> = Pick<ObjectType, Exclude<keyof ObjectType, KeysType>>;
declare type Merge<FirstType, SecondType> = Except<FirstType, Extract<keyof FirstType, keyof SecondType>> & SecondType;
export type GotStreamFunctionOptions = Merge<Options, {
isStream?: true;
}>;
export function defaultGotOptions(): Options {
const { httpsProxy, allowUntrustedCAs } = userStore.preferences;
const rejectUnauthorized = !allowUntrustedCAs;
if (!httpsProxy) {
return { rejectUnauthorized };
}
const https = new HttpsProxyAgent({
keepAlive: true,
keepAliveMsecs: 1000,
maxSockets: 256,
maxFreeSockets: 256,
proxy: httpsProxy,
});
return {
agent: { https },
rejectUnauthorized,
};
}

View File

@ -1,29 +0,0 @@
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<request.Options> {
const { httpsProxy, allowUntrustedCAs } = userStore.preferences;
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);
}

View File

@ -26,7 +26,7 @@ export class BaseClusterDetector {
timeout: 30000, timeout: 30000,
...options, ...options,
headers: { headers: {
Host: `${this.cluster.id}.${new URL(this.cluster.kubeProxyUrl).host}`, // required in ClusterManager.getClusterForRequest() Host: `${this.cluster.id}.${this.cluster.kubeProxyUrl.host}`, // required in ClusterManager.getClusterForRequest()
...(options.headers || {}), ...(options.headers || {}),
}, },
}); });

View File

@ -1,6 +1,5 @@
import { ipcMain } from "electron"; import { ipcMain } from "electron";
import type { ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences } from "../common/cluster-store"; import type { ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences } from "../common/cluster-store";
import type { IMetricsReqParams } from "../renderer/api/endpoints/metrics.api";
import type { WorkspaceId } from "../common/workspace-store"; import type { WorkspaceId } from "../common/workspace-store";
import { action, comparer, computed, observable, reaction, toJS, when } from "mobx"; import { action, comparer, computed, observable, reaction, toJS, when } from "mobx";
import { apiKubePrefix } from "../common/vars"; import { apiKubePrefix } from "../common/vars";
@ -10,12 +9,13 @@ import { AuthorizationV1Api, CoreV1Api, HttpError, KubeConfig, V1ResourceAttribu
import { Kubectl } from "./kubectl"; import { Kubectl } from "./kubectl";
import { KubeconfigManager } from "./kubeconfig-manager"; import { KubeconfigManager } from "./kubeconfig-manager";
import { loadConfig, validateKubeConfig } from "../common/kube-helpers"; import { loadConfig, validateKubeConfig } from "../common/kube-helpers";
import request, { RequestPromiseOptions } from "request-promise-native";
import { apiResources, KubeApiResource } from "../common/rbac"; import { apiResources, KubeApiResource } from "../common/rbac";
import logger from "./logger"; import logger from "./logger";
import { VersionDetector } from "./cluster-detectors/version-detector"; import { VersionDetector } from "./cluster-detectors/version-detector";
import { detectorRegistry } from "./cluster-detectors/detector-registry"; import { detectorRegistry } from "./cluster-detectors/detector-registry";
import plimit from "p-limit"; import plimit from "p-limit";
import got, { OptionsOfJSONResponseBody } from "got";
import { URL } from "url";
export enum ClusterStatus { export enum ClusterStatus {
AccessGranted = 2, AccessGranted = 2,
@ -86,7 +86,7 @@ export class Cluster implements ClusterModel, ClusterState {
whenReady = when(() => this.ready); whenReady = when(() => this.ready);
/** /**
* Is cluster object initializinng on-going * Is cluster object initializing on-going
* *
* @observable * @observable
*/ */
@ -128,7 +128,7 @@ export class Cluster implements ClusterModel, ClusterState {
* @observable * @observable
* @internal * @internal
*/ */
@observable kubeProxyUrl: string; // lens-proxy to kube-api url @observable kubeProxyUrl: URL; // lens-proxy to kube-api url
/** /**
* Is cluster instance enabled (disabled clusters are currently hidden) * Is cluster instance enabled (disabled clusters are currently hidden)
* *
@ -298,7 +298,7 @@ export class Cluster implements ClusterModel, ClusterState {
this.initializing = true; this.initializing = true;
this.contextHandler = new ContextHandler(this); this.contextHandler = new ContextHandler(this);
this.kubeconfigManager = await KubeconfigManager.create(this, this.contextHandler, port); this.kubeconfigManager = await KubeconfigManager.create(this, this.contextHandler, port);
this.kubeProxyUrl = `http://localhost:${port}${apiKubePrefix}`; this.kubeProxyUrl = new URL(`http://localhost:${port}${apiKubePrefix}`);
this.initialized = true; this.initialized = true;
logger.info(`[CLUSTER]: "${this.contextName}" init success`, { logger.info(`[CLUSTER]: "${this.contextName}" init success`, {
id: this.id, id: this.id,
@ -495,31 +495,31 @@ export class Cluster implements ClusterModel, ClusterState {
return this.kubeconfigManager.getPath(); return this.kubeconfigManager.getPath();
} }
protected async k8sRequest<T = any>(path: string, options: RequestPromiseOptions = {}): Promise<T> { private getUrlTo(path: string): URL {
options.headers ??= {}; const url = new URL(this.kubeProxyUrl.toString());
options.json ??= true;
options.timeout ??= 30000;
options.headers.Host = `${this.id}.${new URL(this.kubeProxyUrl).host}`; // required in ClusterManager.getClusterForRequest()
return request(this.kubeProxyUrl + path, options); url.pathname += path;
return url;
} }
/** protected k8sRequest<T = any>(url: URL, options: OptionsOfJSONResponseBody = {}): Promise<T> {
* options.timeout ??= 5000;
* @param prometheusPath path to prometheus service options.headers ??= {};
* @param queryParams query parameters options.headers["content-type"] ??= "application/json";
* @internal options.headers.host ??= `${this.id}.${this.kubeProxyUrl.host}`; // required in ClusterManager.getClusterForRequest()
*/
getMetrics(prometheusPath: string, queryParams: IMetricsReqParams & { query: string }) { return got<T>(url, options).json();
}
getMetrics(prometheusPath: string, searchParams: Record<string, any>) {
const prometheusPrefix = this.preferences.prometheus?.prefix || ""; const prometheusPrefix = this.preferences.prometheus?.prefix || "";
const metricsPath = `/api/v1/namespaces/${prometheusPath}/proxy${prometheusPrefix}/api/v1/query_range`; const metricsPath = `/api/v1/namespaces/${prometheusPath}/proxy${prometheusPrefix}/api/v1/query_range`;
return this.k8sRequest(metricsPath, { return this.k8sRequest(this.getUrlTo(metricsPath), {
timeout: 0, timeout: 0,
resolveWithFullResponse: false,
json: true,
method: "POST", method: "POST",
form: queryParams, form: searchParams,
}); });
} }

View File

@ -1,4 +1,4 @@
import type { PrometheusProvider, PrometheusService } from "./prometheus/provider-registry"; import type { PrometheusService } from "./prometheus/provider-registry";
import type { ClusterPrometheusPreferences } from "../common/cluster-store"; import type { ClusterPrometheusPreferences } from "../common/cluster-store";
import type { Cluster } from "./cluster"; import type { Cluster } from "./cluster";
import type httpProxy from "http-proxy"; import type httpProxy from "http-proxy";
@ -58,14 +58,14 @@ export class ContextHandler {
async getPrometheusService(): Promise<PrometheusService> { async getPrometheusService(): Promise<PrometheusService> {
const providers = this.prometheusProvider ? prometheusProviders.filter(provider => provider.id == this.prometheusProvider) : prometheusProviders; const providers = this.prometheusProvider ? prometheusProviders.filter(provider => provider.id == this.prometheusProvider) : prometheusProviders;
const prometheusPromises: Promise<PrometheusService>[] = providers.map(async (provider: PrometheusProvider): Promise<PrometheusService> => { const prometheusPromises = providers.map(provider => (
const apiClient = (await this.cluster.getProxyKubeconfig()).makeApiClient(CoreV1Api); this.cluster.getProxyKubeconfig()
.then(kubeConfig => kubeConfig.makeApiClient(CoreV1Api))
return await provider.getPrometheusService(apiClient); .then(apiClient => provider.getPrometheusService(apiClient))
}); ));
const resolvedPrometheusServices = await Promise.all(prometheusPromises); const resolvedPrometheusServices = await Promise.all(prometheusPromises);
return resolvedPrometheusServices.filter(n => n)[0]; return resolvedPrometheusServices.find(n => n);
} }
async getPrometheusPath(): Promise<string> { async getPrometheusPath(): Promise<string> {

View File

@ -1,11 +1,13 @@
import yaml from "js-yaml"; import yaml from "js-yaml";
import { readFile } from "fs-extra"; import { readFile } from "fs-extra";
import { promiseExec } from "../promise-exec"; import { promiseExec, stdOptimizedPromiseExec } from "../promise-exec";
import { helmCli } from "./helm-cli"; import { helmCli } from "./helm-cli";
import { Singleton } from "../../common/utils/singleton"; import { Singleton } from "../../common/utils/singleton";
import { customRequestPromise } from "../../common/request"; import { defaultGotOptions } from "../../common/got-opts";
import orderBy from "lodash/orderBy"; import orderBy from "lodash/orderBy";
import logger from "../logger"; import logger from "../logger";
import got, { OptionsOfJSONResponseBody } from "got";
import { filter } from "lodash";
export type HelmEnv = Record<string, string> & { export type HelmEnv = Record<string, string> & {
HELM_REPOSITORY_CACHE?: string; HELM_REPOSITORY_CACHE?: string;
@ -36,14 +38,13 @@ export class HelmRepoManager extends Singleton {
protected initialized: boolean; protected initialized: boolean;
async loadAvailableRepos(): Promise<HelmRepo[]> { async loadAvailableRepos(): Promise<HelmRepo[]> {
const res = await customRequestPromise({ const options = {
uri: "https://github.com/lensapp/artifact-hub-repositories/releases/download/latest/repositories.json", ...defaultGotOptions() as OptionsOfJSONResponseBody,
json: true, timeout: 10000
resolveWithFullResponse: true, };
timeout: 10000, const r = await got<HelmRepo[]>("https://github.com/lensapp/artifact-hub-repositories/releases/download/latest/repositories.json", options);
});
return orderBy<HelmRepo>(res.body, repo => repo.name); return orderBy(r.body, repo => repo.name);
} }
async init() { async init() {
@ -60,7 +61,7 @@ export class HelmRepoManager extends Singleton {
protected async parseHelmEnv() { protected async parseHelmEnv() {
const helm = await helmCli.binaryPath(); const helm = await helmCli.binaryPath();
const { stdout } = await promiseExec(`"${helm}" env`).catch((error) => { const { stdout } = await promiseExec(`"${helm}" env`).catch((error) => {
throw(error.stderr); throw (error.stderr);
}); });
const lines = stdout.split(/\r?\n/); // split by new line feed const lines = stdout.split(/\r?\n/); // split by new line feed
const env: HelmEnv = {}; const env: HelmEnv = {};
@ -114,50 +115,41 @@ export class HelmRepoManager extends Singleton {
public async update() { public async update() {
const helm = await helmCli.binaryPath(); const helm = await helmCli.binaryPath();
const { stdout } = await promiseExec(`"${helm}" repo update`).catch((error) => { const command = `"${helm}" repo update`;
return { stdout: error.stdout };
});
return stdout; return stdOptimizedPromiseExec(command);
} }
public async addRepo({ name, url }: HelmRepo) { public async addRepo({ name, url }: HelmRepo) {
logger.info(`[HELM]: adding repo "${name}" from ${url}`); logger.info(`[HELM]: adding repo "${name}" from ${url}`);
const helm = await helmCli.binaryPath(); const helm = await helmCli.binaryPath();
const { stdout } = await promiseExec(`"${helm}" repo add ${name} ${url}`).catch((error) => { const command = `"${helm}" repo add ${name} ${url}`;
throw(error.stderr);
});
return stdout; return stdOptimizedPromiseExec(command);
} }
public async addСustomRepo(repoAttributes : HelmRepo) { public async addСustomRepo(repoAttributes : HelmRepo) {
logger.info(`[HELM]: adding repo "${repoAttributes.name}" from ${repoAttributes.url}`); logger.info(`[HELM]: adding repo "${repoAttributes.name}" from ${repoAttributes.url}`);
const helm = await helmCli.binaryPath(); const helm = await helmCli.binaryPath();
const args = filter([
repoAttributes.insecureSkipTlsVerify && "--insecure-skip-tls-verify",
repoAttributes.username && `--username "${repoAttributes.username}"`,
repoAttributes.username && `--username "${repoAttributes.username}"`,
repoAttributes.caFile && `--ca-file "${repoAttributes.caFile}"`,
repoAttributes.keyFile && `--key-file "${repoAttributes.keyFile}"`,
repoAttributes.certFile && `--cert-file "${repoAttributes.certFile}"`,
]);
const command = `"${helm}" repo add ${repoAttributes.name} ${repoAttributes.url} ${args.join(" ")}`;
const insecureSkipTlsVerify = repoAttributes.insecureSkipTlsVerify ? " --insecure-skip-tls-verify" : ""; return stdOptimizedPromiseExec(command);
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}"` : "";
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);
});
return stdout;
} }
public async removeRepo({ name, url }: HelmRepo): Promise<string> { public async removeRepo({ name, url }: HelmRepo): Promise<string> {
logger.info(`[HELM]: removing repo "${name}" from ${url}`); logger.info(`[HELM]: removing repo "${name}" from ${url}`);
const helm = await helmCli.binaryPath(); const helm = await helmCli.binaryPath();
const { stdout } = await promiseExec(`"${helm}" repo remove ${name}`).catch((error) => { const command = `"${helm}" repo remove ${name}`;
throw(error.stderr);
});
return stdout; return stdOptimizedPromiseExec(command);
} }
} }

View File

@ -1,15 +1,21 @@
import { app, remote } from "electron"; import { app, remote } from "electron";
import path from "path";
import fs from "fs"; import fs from "fs";
import { promiseExec } from "./promise-exec";
import logger from "./logger";
import { ensureDir, pathExists } from "fs-extra"; import { ensureDir, pathExists } from "fs-extra";
import got from "got";
import path from "path";
import * as lockFile from "proper-lockfile"; import * as lockFile from "proper-lockfile";
import { helmCli } from "./helm/helm-cli"; import stream from "stream";
import { promisify } from "util";
import { defaultGotOptions, GotStreamFunctionOptions } from "../common/got-opts";
import { userStore } from "../common/user-store"; import { userStore } from "../common/user-store";
import { customRequest } from "../common/request";
import { getBundledKubectlVersion } from "../common/utils/app-version"; import { getBundledKubectlVersion } from "../common/utils/app-version";
import { isDevelopment, isWindows, isTestEnv } from "../common/vars"; import { isDevelopment, isTestEnv, isWindows } from "../common/vars";
import { helmCli } from "./helm/helm-cli";
import logger from "./logger";
import { promiseExec } from "./promise-exec";
const pipeline = promisify(stream.pipeline);
const bundledVersion = getBundledKubectlVersion(); const bundledVersion = getBundledKubectlVersion();
const kubectlMap: Map<string, string> = new Map([ const kubectlMap: Map<string, string> = new Map([
@ -273,33 +279,22 @@ export class Kubectl {
logger.info(`Downloading kubectl ${this.kubectlVersion} from ${this.url} to ${this.path}`); logger.info(`Downloading kubectl ${this.kubectlVersion} from ${this.url} to ${this.path}`);
return new Promise((resolve, reject) => { logger.info(`Downloading kubectl ${this.kubectlVersion} from ${this.url} to ${this.path}`);
const stream = customRequest({ const options: GotStreamFunctionOptions = {
url: this.url, ...defaultGotOptions(),
gzip: true, decompress: true,
}); isStream: true,
const file = fs.createWriteStream(this.path); };
stream.on("complete", () => { try {
logger.debug("kubectl binary download finished"); // TODO: improve the UI for this to use some sort of loading bar TUI
file.end(); await pipeline(
}); got.stream(this.url, options),
stream.on("error", (error) => { fs.createWriteStream(this.path)
logger.error(error); );
fs.unlink(this.path, () => { } catch (err) {
// do nothing logger.error("Failed to download kubectl:", err);
}); }
reject(error);
});
file.on("close", () => {
logger.debug("kubectl binary download closed");
fs.chmod(this.path, 0o755, (err) => {
if (err) reject(err);
});
resolve();
});
stream.pipe(file);
});
} }
protected async writeInitScripts() { protected async writeInitScripts() {

View File

@ -1,17 +1,22 @@
import path from "path"; import path from "path";
import fs from "fs"; import fs from "fs";
import request from "request"; import fse from "fs-extra";
import { ensureDir, pathExists } from "fs-extra";
import * as tar from "tar"; import * as tar from "tar";
import { isWindows } from "../common/vars"; import { isWindows } from "../common/vars";
import winston from "winston"; import winston from "winston";
import { GotStreamFunctionOptions } from "../common/got-opts";
import got from "got";
import stream from "stream";
import { promisify } from "util";
const pipeline = promisify(stream.pipeline);
export type LensBinaryOpts = { export type LensBinaryOpts = {
version: string; version: string;
baseDir: string; baseDir: string;
originalBinaryName: string; originalBinaryName: string;
newBinaryName?: string; newBinaryName?: string;
requestOpts?: request.Options; requestOpts?: GotStreamFunctionOptions;
}; };
export class LensBinary { export class LensBinary {
@ -26,7 +31,7 @@ export class LensBinary {
protected platformName: string; protected platformName: string;
protected arch: string; protected arch: string;
protected originalBinaryName: string; protected originalBinaryName: string;
protected requestOpts: request.Options; protected requestOpts: GotStreamFunctionOptions;
protected logger: Console | winston.Logger; protected logger: Console | winston.Logger;
constructor(opts: LensBinaryOpts) { constructor(opts: LensBinaryOpts) {
@ -63,7 +68,7 @@ export class LensBinary {
} }
} }
public setLogger(logger: Console | winston.Logger) { public setLogger(logger: Console | winston.Logger) {
this.logger = logger; this.logger = logger;
} }
@ -110,88 +115,71 @@ export class LensBinary {
} }
protected async checkBinary() { protected async checkBinary() {
const exists = await pathExists(this.getBinaryPath()); return fse.pathExists(this.getBinaryPath());
return exists;
} }
public async ensureBinary() { public async ensureBinary() {
const isValid = await this.checkBinary(); try {
if (await this.checkBinary()) {
return;
}
await this.downloadBinary();
if (this.tarPath) {
await this.untarBinary();
}
if (this.originalBinaryName !== this.binaryName) {
await this.renameBinary();
}
if (!isValid) {
await this.downloadBinary().catch((error) => {
this.logger.error(error);
});
if (this.tarPath) await this.untarBinary();
if (this.originalBinaryName != this.binaryName) await this.renameBinary();
this.logger.info(`${this.originalBinaryName} has been downloaded to ${this.getBinaryPath()}`); this.logger.info(`${this.originalBinaryName} has been downloaded to ${this.getBinaryPath()}`);
} catch (err) {
this.logger.error(err);
} }
} }
protected async untarBinary() { protected async untarBinary() {
return new Promise<void>(resolve => { this.logger.debug(`Extracting ${this.originalBinaryName} binary`);
this.logger.debug(`Extracting ${this.originalBinaryName} binary`);
tar.x({ try {
await tar.x({
file: this.tarPath, file: this.tarPath,
cwd: this.dirname cwd: this.dirname
}).then((() => { });
resolve(); } catch (err) {
})); this.logger.error(`failed to extract ${this.originalBinaryName}: `, err);
}); }
} }
protected async renameBinary() { protected async renameBinary() {
return new Promise<void>((resolve, reject) => { this.logger.debug(`Renaming ${this.originalBinaryName} binary to ${this.binaryName}`);
this.logger.debug(`Renaming ${this.originalBinaryName} binary to ${this.binaryName}`);
fs.rename(this.getOriginalBinaryPath(), this.getBinaryPath(), (err) => { return fse.promises.rename(this.getOriginalBinaryPath(), this.getBinaryPath());
if (err) {
reject(err);
}
else {
resolve();
}
});
});
} }
protected async downloadBinary() { protected async downloadBinary() {
const binaryPath = this.tarPath || this.getBinaryPath(); const binaryPath = this.tarPath || this.getBinaryPath();
await ensureDir(this.getBinaryDir(), 0o755); await fse.ensureDir(this.getBinaryDir(), 0o755);
const file = fs.createWriteStream(binaryPath);
const url = this.getUrl(); const url = this.getUrl();
const requestOpts: GotStreamFunctionOptions = {
this.logger.info(`Downloading ${this.originalBinaryName} ${this.binaryVersion} from ${url} to ${binaryPath}`); decompress: true,
const requestOpts: request.UriOptions & request.CoreOptions = { isStream: true,
uri: url,
gzip: true,
...this.requestOpts ...this.requestOpts
}; };
const stream = request(requestOpts);
stream.on("complete", () => { this.logger.info(`Downloading ${this.originalBinaryName} ${this.binaryVersion} from ${url} to ${binaryPath}`);
this.logger.info(`Download of ${this.originalBinaryName} finished`);
file.end();
});
stream.on("error", (error) => { try {
this.logger.error(error); await pipeline(
fs.unlink(binaryPath, () => { got.stream(url, requestOpts),
// do nothing fs.createWriteStream(binaryPath),
}); );
throw(error); } catch (err) {
}); this.logger.error("Failed to download kubectl:", err);
}
return new Promise((resolve, reject) => {
file.on("close", () => {
this.logger.debug(`${this.originalBinaryName} binary download closed`);
if (!this.tarPath) fs.chmod(binaryPath, 0o755, (err) => {
if (err) reject(err);
});
resolve();
});
stream.pipe(file);
});
} }
} }

View File

@ -1,6 +1,6 @@
import { CoreV1Api } from "@kubernetes/client-node"; import { CoreV1Api } from "@kubernetes/client-node";
export type PrometheusClusterQuery = { export interface PrometheusClusterQuery {
memoryUsage: string; memoryUsage: string;
memoryRequests: string; memoryRequests: string;
memoryLimits: string; memoryLimits: string;
@ -11,18 +11,18 @@ export type PrometheusClusterQuery = {
cpuCapacity: string; cpuCapacity: string;
podUsage: string; podUsage: string;
podCapacity: string; podCapacity: string;
}; }
export type PrometheusNodeQuery = { export interface PrometheusNodeQuery {
memoryUsage: string; memoryUsage: string;
memoryCapacity: string; memoryCapacity: string;
cpuUsage: string; cpuUsage: string;
cpuCapacity: string; cpuCapacity: string;
fsSize: string; fsSize: string;
fsUsage: string; fsUsage: string;
}; }
export type PrometheusPodQuery = { export interface PrometheusPodQuery {
memoryUsage: string; memoryUsage: string;
memoryRequests: string; memoryRequests: string;
memoryLimits: string; memoryLimits: string;
@ -32,25 +32,26 @@ export type PrometheusPodQuery = {
fsUsage: string; fsUsage: string;
networkReceive: string; networkReceive: string;
networkTransmit: string; networkTransmit: string;
}; }
export type PrometheusPvcQuery = { export interface PrometheusPvcQuery {
diskUsage: string; diskUsage: string;
diskCapacity: string; diskCapacity: string;
}; }
export type PrometheusIngressQuery = { export interface PrometheusIngressQuery {
bytesSentSuccess: string; bytesSentSuccess: string;
bytesSentFailure: string; bytesSentFailure: string;
requestDurationSeconds: string; requestDurationSeconds: string;
responseDurationSeconds: string; responseDurationSeconds: string;
}; }
export type PrometheusQueryOpts = { export type PrometheusQueryOpts = {
[key: string]: string | any; [key: string]: string | any;
}; };
export type PrometheusQuery = PrometheusNodeQuery | PrometheusClusterQuery | PrometheusPodQuery | PrometheusPvcQuery | PrometheusIngressQuery; export type PrometheusQuery = PrometheusNodeQuery | PrometheusClusterQuery | PrometheusPodQuery | PrometheusPvcQuery | PrometheusIngressQuery;
export type PrometheusQueryKey = keyof PrometheusNodeQuery | keyof PrometheusClusterQuery | keyof PrometheusPodQuery | keyof PrometheusPvcQuery | keyof PrometheusIngressQuery;
export type PrometheusService = { export type PrometheusService = {
id: string; id: string;

View File

@ -2,3 +2,13 @@ import * as util from "util";
import { exec } from "child_process"; import { exec } from "child_process";
export const promiseExec = util.promisify(exec); export const promiseExec = util.promisify(exec);
export async function stdOptimizedPromiseExec(url: string): Promise<string> {
try {
const { stdout } = await promiseExec(url);
return stdout;
} catch ({ stderr }) {
throw stderr;
}
}

View File

@ -52,7 +52,7 @@ class HelmApiRoute extends LensApi {
const { cluster, params, payload, response } = request; const { cluster, params, payload, response } = request;
try { try {
const result = await helmService.updateRelease(cluster, params.release, params.namespace, payload ); const result = await helmService.updateRelease(cluster, params.release, params.namespace, payload);
this.respondJson(response, result); this.respondJson(response, result);
} catch (error) { } catch (error) {
@ -81,7 +81,7 @@ class HelmApiRoute extends LensApi {
const result = await helmService.listReleases(cluster, params.namespace); const result = await helmService.listReleases(cluster, params.namespace);
this.respondJson(response, result); this.respondJson(response, result);
} catch(error) { } catch (error) {
logger.debug(error); logger.debug(error);
this.respondText(response, error, 422); this.respondText(response, error, 422);
} }

View File

@ -4,10 +4,15 @@ import { LensApi } from "../lens-api";
import { Cluster, ClusterMetadataKey } from "../cluster"; import { Cluster, ClusterMetadataKey } from "../cluster";
import { ClusterPrometheusMetadata } from "../../common/cluster-store"; import { ClusterPrometheusMetadata } from "../../common/cluster-store";
import logger from "../logger"; import logger from "../logger";
import { PrometheusQueryKey } from "../prometheus/provider-registry";
export type IMetricsQuery = string | string[] | { export interface MetricsQuery {
[metricName: string]: string; [metricName: string]: string;
}; }
function delay(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// This is used for backoff retry tracking. // This is used for backoff retry tracking.
const MAX_ATTEMPTS = 5; const MAX_ATTEMPTS = 5;
@ -29,7 +34,7 @@ async function loadMetrics(promQueries: string[], cluster: Cluster, prometheusPa
throw new Error("Metrics not available"); throw new Error("Metrics not available");
} }
await new Promise(resolve => setTimeout(resolve, (attempt + 1) * 1000)); // add delay before repeating request await delay((attempt + 1) * 1000); // add delay before repeating request
} }
} }
} }
@ -40,9 +45,14 @@ async function loadMetrics(promQueries: string[], cluster: Cluster, prometheusPa
return Promise.all(queries.map(loadMetric)); return Promise.all(queries.map(loadMetric));
} }
export interface MetricsResult {
status?: string;
data?: any;
}
class MetricsRoute extends LensApi { class MetricsRoute extends LensApi {
async routeMetrics({ response, cluster, payload, query }: LensApiRequest) { async routeMetrics({ response, cluster, payload, query }: LensApiRequest) {
const queryParams: IMetricsQuery = Object.fromEntries(query.entries()); const queryParams: MetricsQuery = Object.fromEntries(query.entries());
const prometheusMetadata: ClusterPrometheusMetadata = {}; const prometheusMetadata: ClusterPrometheusMetadata = {};
try { try {
@ -72,7 +82,7 @@ class MetricsRoute extends LensApi {
this.respondJson(response, data); this.respondJson(response, data);
} else { } else {
const queries = Object.entries(payload).map(([queryName, queryOpts]) => ( const queries = Object.entries(payload).map(([queryName, queryOpts]) => (
(prometheusProvider.getQueries(queryOpts) as Record<string, string>)[queryName] (prometheusProvider.getQueries(queryOpts) as Record<PrometheusQueryKey, string>)[queryName as PrometheusQueryKey]
)); ));
const result = await loadMetrics(queries, cluster, prometheusPath, queryParams); const result = await loadMetrics(queries, cluster, prometheusPath, queryParams);
const data = Object.fromEntries(Object.keys(payload).map((metricName, i) => [metricName, result[i]])); const data = Object.fromEntries(Object.keys(payload).map((metricName, i) => [metricName, result[i]]));

View File

@ -2,7 +2,7 @@
import moment from "moment"; import moment from "moment";
import { apiBase } from "../index"; import { apiBase } from "../index";
import type { IMetricsQuery } from "../../../main/routes/metrics-route"; import type { MetricsQuery } from "../../../main/routes/metrics-route";
export interface IMetrics { export interface IMetrics {
status: string; status: string;
@ -34,7 +34,7 @@ export interface IMetricsReqParams {
} }
export const metricsApi = { export const metricsApi = {
async getMetrics<T = IMetricsQuery>(query: T, reqParams: IMetricsReqParams = {}): Promise<T extends object ? { [K in keyof T]: IMetrics } : IMetrics> { async getMetrics<T = MetricsQuery>(query: T, reqParams: IMetricsReqParams = {}): Promise<T extends object ? { [K in keyof T]: IMetrics } : IMetrics> {
const { range = 3600, step = 60, namespace } = reqParams; const { range = 3600, step = 60, namespace } = reqParams;
let { start, end } = reqParams; let { start, end } = reqParams;

View File

@ -13,7 +13,8 @@ export default function (): webpack.Configuration {
context: __dirname, context: __dirname,
target: "electron-main", target: "electron-main",
mode: isProduction ? "production" : "development", mode: isProduction ? "production" : "development",
devtool: isProduction ? "source-map" : "cheap-eval-source-map", // devtool: isProduction ? "source-map" : "cheap-eval-source-map",
devtool: "source-map",
cache: isDevelopment, cache: isDevelopment,
entry: { entry: {
main: path.resolve(mainDir, "index.ts"), main: path.resolve(mainDir, "index.ts"),

129
yarn.lock
View File

@ -972,6 +972,11 @@
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==
"@sindresorhus/is@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.0.tgz#2ff674e9611b45b528896d820d3d7a812de2f0e4"
integrity sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ==
"@sinonjs/commons@^1.7.0": "@sinonjs/commons@^1.7.0":
version "1.8.0" version "1.8.0"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.0.tgz#c8d68821a854c555bba172f3b06959a0039b236d" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.0.tgz#c8d68821a854c555bba172f3b06959a0039b236d"
@ -993,10 +998,17 @@
dependencies: dependencies:
defer-to-connect "^1.0.1" defer-to-connect "^1.0.1"
"@szmarczak/http-timer@^4.0.5":
version "4.0.5"
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152"
integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==
dependencies:
defer-to-connect "^2.0.0"
"@testing-library/dom@>=7": "@testing-library/dom@>=7":
version "7.29.4" version "7.30.0"
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.29.4.tgz#1647c2b478789621ead7a50614ad81ab5ae5b86c" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.30.0.tgz#53697851f7708a1448cc30b74a2ea056dd709cd6"
integrity sha512-CtrJRiSYEfbtNGtEsd78mk1n1v2TUbeABlNIcOCJdDfkN5/JTOwQEbbQpoSRxGqzcWPgStMvJ4mNolSuBRv1NA== integrity sha512-v4GzWtltaiDE0yRikLlcLAfEiiK8+ptu6OuuIebm9GdC2XlZTNDPGEfM2UkEtnH7hr9TRq2sivT5EA9P1Oy7bw==
dependencies: dependencies:
"@babel/code-frame" "^7.10.4" "@babel/code-frame" "^7.10.4"
"@babel/runtime" "^7.12.5" "@babel/runtime" "^7.12.5"
@ -1106,6 +1118,16 @@
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
"@types/cacheable-request@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976"
integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==
dependencies:
"@types/http-cache-semantics" "*"
"@types/keyv" "*"
"@types/node" "*"
"@types/responselike" "*"
"@types/caseless@*": "@types/caseless@*":
version "0.12.2" version "0.12.2"
resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8" resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8"
@ -1308,6 +1330,11 @@
"@types/tapable" "*" "@types/tapable" "*"
"@types/webpack" "*" "@types/webpack" "*"
"@types/http-cache-semantics@*":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a"
integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==
"@types/http-proxy-middleware@*": "@types/http-proxy-middleware@*":
version "0.19.3" version "0.19.3"
resolved "https://registry.yarnpkg.com/@types/http-proxy-middleware/-/http-proxy-middleware-0.19.3.tgz#b2eb96fbc0f9ac7250b5d9c4c53aade049497d03" resolved "https://registry.yarnpkg.com/@types/http-proxy-middleware/-/http-proxy-middleware-0.19.3.tgz#b2eb96fbc0f9ac7250b5d9c4c53aade049497d03"
@ -1411,6 +1438,13 @@
resolved "https://registry.yarnpkg.com/@types/jsonpath/-/jsonpath-0.2.0.tgz#13c62db22a34d9c411364fac79fd374d63445aa1" resolved "https://registry.yarnpkg.com/@types/jsonpath/-/jsonpath-0.2.0.tgz#13c62db22a34d9c411364fac79fd374d63445aa1"
integrity sha512-v7qlPA0VpKUlEdhghbDqRoKMxFB3h3Ch688TApBJ6v+XLDdvWCGLJIYiPKGZnS6MAOie+IorCfNYVHOPIHSWwQ== integrity sha512-v7qlPA0VpKUlEdhghbDqRoKMxFB3h3Ch688TApBJ6v+XLDdvWCGLJIYiPKGZnS6MAOie+IorCfNYVHOPIHSWwQ==
"@types/keyv@*":
version "3.1.1"
resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7"
integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==
dependencies:
"@types/node" "*"
"@types/lodash@^4.14.155": "@types/lodash@^4.14.155":
version "4.14.155" version "4.14.155"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.155.tgz#e2b4514f46a261fd11542e47519c20ebce7bc23a" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.155.tgz#e2b4514f46a261fd11542e47519c20ebce7bc23a"
@ -1665,6 +1699,13 @@
"@types/tough-cookie" "*" "@types/tough-cookie" "*"
form-data "^2.5.0" form-data "^2.5.0"
"@types/responselike@*", "@types/responselike@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29"
integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==
dependencies:
"@types/node" "*"
"@types/retry@*": "@types/retry@*":
version "0.12.0" version "0.12.0"
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
@ -3262,6 +3303,11 @@ cache-base@^1.0.1:
union-value "^1.0.0" union-value "^1.0.0"
unset-value "^1.0.0" unset-value "^1.0.0"
cacheable-lookup@^5.0.3:
version "5.0.4"
resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005"
integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==
cacheable-request@^2.1.1: cacheable-request@^2.1.1:
version "2.1.4" version "2.1.4"
resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d"
@ -3288,6 +3334,19 @@ cacheable-request@^6.0.0:
normalize-url "^4.1.0" normalize-url "^4.1.0"
responselike "^1.0.2" responselike "^1.0.2"
cacheable-request@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.1.tgz#062031c2856232782ed694a257fa35da93942a58"
integrity sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==
dependencies:
clone-response "^1.0.2"
get-stream "^5.1.0"
http-cache-semantics "^4.0.0"
keyv "^4.0.0"
lowercase-keys "^2.0.0"
normalize-url "^4.1.0"
responselike "^2.0.0"
call-bind@^1.0.0: call-bind@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce"
@ -4436,6 +4495,11 @@ defer-to-connect@^1.0.1:
resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591"
integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==
defer-to-connect@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.0.tgz#83d6b199db041593ac84d781b5222308ccf4c2c1"
integrity sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==
define-properties@^1.1.2, define-properties@^1.1.3: define-properties@^1.1.2, define-properties@^1.1.3:
version "1.1.3" version "1.1.3"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
@ -6328,6 +6392,23 @@ globule@^1.0.0:
lodash "~4.17.10" lodash "~4.17.10"
minimatch "~3.0.2" minimatch "~3.0.2"
got@^11.8.1:
version "11.8.1"
resolved "https://registry.yarnpkg.com/got/-/got-11.8.1.tgz#df04adfaf2e782babb3daabc79139feec2f7e85d"
integrity sha512-9aYdZL+6nHmvJwHALLwKSUZ0hMwGaJGYv3hoPLPgnT8BoBXm1SjnZeky+91tfwJaDzun2s4RsBRy48IEYv2q2Q==
dependencies:
"@sindresorhus/is" "^4.0.0"
"@szmarczak/http-timer" "^4.0.5"
"@types/cacheable-request" "^6.0.1"
"@types/responselike" "^1.0.0"
cacheable-lookup "^5.0.3"
cacheable-request "^7.0.1"
decompress-response "^6.0.0"
http2-wrapper "^1.0.0-beta.5.2"
lowercase-keys "^2.0.0"
p-cancelable "^2.0.0"
responselike "^2.0.0"
got@^6.7.1: got@^6.7.1:
version "6.7.1" version "6.7.1"
resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0"
@ -6606,6 +6687,11 @@ hpack.js@^2.1.6:
readable-stream "^2.0.1" readable-stream "^2.0.1"
wbuf "^1.1.0" wbuf "^1.1.0"
hpagent@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/hpagent/-/hpagent-0.1.1.tgz#66f67f16e5c7a8b59a068e40c2658c2c749ad5e2"
integrity sha512-IxJWQiY0vmEjetHdoE9HZjD4Cx+mYTr25tR7JCxXaiI3QxW0YqYyM11KyZbHufoa/piWhMb2+D3FGpMgmA2cFQ==
html-encoding-sniffer@^2.0.1: html-encoding-sniffer@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3"
@ -6751,6 +6837,14 @@ http-signature@~1.2.0:
jsprim "^1.2.2" jsprim "^1.2.2"
sshpk "^1.7.0" sshpk "^1.7.0"
http2-wrapper@^1.0.0-beta.5.2:
version "1.0.0-beta.5.2"
resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz#8b923deb90144aea65cf834b016a340fc98556f3"
integrity sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==
dependencies:
quick-lru "^5.1.1"
resolve-alpn "^1.0.0"
https-browserify@^1.0.0: https-browserify@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
@ -8114,6 +8208,11 @@ json-buffer@3.0.0:
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=
json-buffer@3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
@ -8315,6 +8414,13 @@ keyv@^3.0.0:
dependencies: dependencies:
json-buffer "3.0.0" json-buffer "3.0.0"
keyv@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.3.tgz#4f3aa98de254803cafcd2896734108daa35e4254"
integrity sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==
dependencies:
json-buffer "3.0.1"
killable@^1.0.1: killable@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
@ -11177,6 +11283,11 @@ querystringify@^2.1.1:
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
quick-lru@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
qw@~1.0.1: qw@~1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/qw/-/qw-1.0.1.tgz#efbfdc740f9ad054304426acb183412cc8b996d4" resolved "https://registry.yarnpkg.com/qw/-/qw-1.0.1.tgz#efbfdc740f9ad054304426acb183412cc8b996d4"
@ -11761,6 +11872,11 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
resolve-alpn@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.0.0.tgz#745ad60b3d6aff4b4a48e01b8c0bdc70959e0e8c"
integrity sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA==
resolve-cwd@^2.0.0: resolve-cwd@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
@ -11830,6 +11946,13 @@ responselike@1.0.2, responselike@^1.0.2:
dependencies: dependencies:
lowercase-keys "^1.0.0" lowercase-keys "^1.0.0"
responselike@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723"
integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==
dependencies:
lowercase-keys "^2.0.0"
restore-cursor@^2.0.0: restore-cursor@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"