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:
parent
33c405bdcf
commit
47fbf629ea
@ -1,10 +1,16 @@
|
||||
import packageInfo from "../package.json";
|
||||
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 requestPromise from "request-promise-native";
|
||||
import { ensureDir, pathExists } from "fs-extra";
|
||||
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 {
|
||||
public kubectlVersion: string;
|
||||
@ -22,21 +28,26 @@ class KubectlDownloader {
|
||||
}
|
||||
|
||||
protected async urlEtag() {
|
||||
const response = await requestPromise({
|
||||
method: "HEAD",
|
||||
uri: this.url,
|
||||
resolveWithFullResponse: true
|
||||
}).catch((error) => { console.log(error); });
|
||||
try {
|
||||
const res = await got.head(this.url);
|
||||
const { etag } = res.headers;
|
||||
|
||||
if (response.headers["etag"]) {
|
||||
return response.headers["etag"].replace(/"/g, "");
|
||||
if (Array.isArray(etag)) {
|
||||
return etag[0].replace(/"/g, "");
|
||||
}
|
||||
|
||||
if (typeof etag === "string") {
|
||||
return etag.replace(/"/g, "");
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error("Failed to get etag:", err);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public async checkBinary() {
|
||||
const exists = await pathExists(this.path);
|
||||
const exists = await fse.pathExists(this.path);
|
||||
|
||||
if (exists) {
|
||||
const hash = md5File.sync(this.path);
|
||||
@ -55,65 +66,57 @@ class KubectlDownloader {
|
||||
return false;
|
||||
}
|
||||
|
||||
public async downloadKubectl() {
|
||||
public async downloadKubectl(): Promise<void> {
|
||||
const exists = await this.checkBinary();
|
||||
|
||||
if(exists) {
|
||||
console.log("Already exists and is valid");
|
||||
|
||||
return;
|
||||
if (exists) {
|
||||
return void console.log("Already exists and is valid");
|
||||
}
|
||||
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}`);
|
||||
const requestOpts: request.UriOptions & request.CoreOptions = {
|
||||
uri: this.url,
|
||||
gzip: true
|
||||
const options: GotStreamFunctionOptions = {
|
||||
decompress: true,
|
||||
isStream: true,
|
||||
};
|
||||
const stream = request(requestOpts);
|
||||
|
||||
stream.on("complete", () => {
|
||||
console.log("kubectl binary download finished");
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
file.end(() => {});
|
||||
});
|
||||
|
||||
stream.on("error", (error) => {
|
||||
console.log(error);
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
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);
|
||||
});
|
||||
try {
|
||||
// TODO: improve the UI for this to use some sort of loading bar TUI
|
||||
await pipeline(
|
||||
got.stream(this.url, options),
|
||||
fs.createWriteStream(this.path)
|
||||
);
|
||||
await fse.chmod(this.path, 0o755);
|
||||
} catch (err) {
|
||||
logger.error("Failed to download kubectl:", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const downloadVersion = packageInfo.config.bundledKubectlVersion;
|
||||
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: "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: "386", target: path.join(baseDir, "windows", "ia32", "kubectl.exe") }
|
||||
];
|
||||
|
||||
downloads.forEach((dlOpts) => {
|
||||
console.log(dlOpts);
|
||||
const downloader = new KubectlDownloader(downloadVersion, dlOpts.platform, dlOpts.arch, dlOpts.target);
|
||||
async function downloadOne(opts: DownloadTarget) {
|
||||
const downloader = new KubectlDownloader(downloadVersion, opts.platform, opts.arch, opts.target);
|
||||
|
||||
console.log(`Downloading: ${JSON.stringify(dlOpts)}`);
|
||||
downloader.downloadKubectl().then(() => downloader.checkBinary().then(() => console.log("Download complete")));
|
||||
});
|
||||
console.log(`Downloading: ${util.inspect(opts, false, null, true)}`);
|
||||
await downloader.downloadKubectl();
|
||||
await downloader.checkBinary();
|
||||
console.log(`Finished downloading for ${opts.platform}/${opts.arch}`);
|
||||
}
|
||||
|
||||
Promise.all(downloads.map(downloadOne));
|
||||
|
||||
@ -13,7 +13,7 @@ import * as utils from "../helpers/utils";
|
||||
import { listHelmRepositories } from "../helpers/utils";
|
||||
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)
|
||||
describe("Lens integration tests", () => {
|
||||
|
||||
@ -49,12 +49,13 @@ export function describeIf(condition: boolean) {
|
||||
return condition ? describe : describe.skip;
|
||||
}
|
||||
|
||||
export function setup(): Application {
|
||||
export function setup(waitTimeout = 60000): Application {
|
||||
return new Application({
|
||||
path: AppPaths[process.platform], // path to electron app
|
||||
args: [],
|
||||
startTimeout: 30000,
|
||||
waitTimeout: 60000,
|
||||
waitTimeout,
|
||||
chromeDriverArgs: ["remote-debugging-port=9222"],
|
||||
env: {
|
||||
CICD: "true"
|
||||
}
|
||||
@ -101,7 +102,7 @@ export async function tearDown(app: Application) {
|
||||
await app.stop();
|
||||
|
||||
try {
|
||||
process.kill(pid, "SIGKILL");
|
||||
process.kill(pid, 0);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
@ -207,7 +207,9 @@
|
||||
"electron-window-state": "^5.0.3",
|
||||
"filenamify": "^4.1.0",
|
||||
"fs-extra": "^9.0.1",
|
||||
"got": "^11.8.1",
|
||||
"handlebars": "^4.7.6",
|
||||
"hpagent": "^0.1.1",
|
||||
"http-proxy": "^1.18.1",
|
||||
"immer": "^8.0.1",
|
||||
"js-yaml": "^3.14.0",
|
||||
|
||||
31
src/common/got-opts.ts
Normal file
31
src/common/got-opts.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -26,7 +26,7 @@ export class BaseClusterDetector {
|
||||
timeout: 30000,
|
||||
...options,
|
||||
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 || {}),
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { ipcMain } from "electron";
|
||||
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 { action, comparer, computed, observable, reaction, toJS, when } from "mobx";
|
||||
import { apiKubePrefix } from "../common/vars";
|
||||
@ -10,12 +9,13 @@ import { AuthorizationV1Api, CoreV1Api, HttpError, KubeConfig, V1ResourceAttribu
|
||||
import { Kubectl } from "./kubectl";
|
||||
import { KubeconfigManager } from "./kubeconfig-manager";
|
||||
import { loadConfig, validateKubeConfig } from "../common/kube-helpers";
|
||||
import request, { RequestPromiseOptions } from "request-promise-native";
|
||||
import { apiResources, KubeApiResource } from "../common/rbac";
|
||||
import logger from "./logger";
|
||||
import { VersionDetector } from "./cluster-detectors/version-detector";
|
||||
import { detectorRegistry } from "./cluster-detectors/detector-registry";
|
||||
import plimit from "p-limit";
|
||||
import got, { OptionsOfJSONResponseBody } from "got";
|
||||
import { URL } from "url";
|
||||
|
||||
export enum ClusterStatus {
|
||||
AccessGranted = 2,
|
||||
@ -86,7 +86,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
||||
whenReady = when(() => this.ready);
|
||||
|
||||
/**
|
||||
* Is cluster object initializinng on-going
|
||||
* Is cluster object initializing on-going
|
||||
*
|
||||
* @observable
|
||||
*/
|
||||
@ -128,7 +128,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
||||
* @observable
|
||||
* @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)
|
||||
*
|
||||
@ -298,7 +298,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
||||
this.initializing = true;
|
||||
this.contextHandler = new ContextHandler(this);
|
||||
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;
|
||||
logger.info(`[CLUSTER]: "${this.contextName}" init success`, {
|
||||
id: this.id,
|
||||
@ -495,31 +495,31 @@ export class Cluster implements ClusterModel, ClusterState {
|
||||
return this.kubeconfigManager.getPath();
|
||||
}
|
||||
|
||||
protected async k8sRequest<T = any>(path: string, options: RequestPromiseOptions = {}): Promise<T> {
|
||||
options.headers ??= {};
|
||||
options.json ??= true;
|
||||
options.timeout ??= 30000;
|
||||
options.headers.Host = `${this.id}.${new URL(this.kubeProxyUrl).host}`; // required in ClusterManager.getClusterForRequest()
|
||||
private getUrlTo(path: string): URL {
|
||||
const url = new URL(this.kubeProxyUrl.toString());
|
||||
|
||||
return request(this.kubeProxyUrl + path, options);
|
||||
url.pathname += path;
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param prometheusPath path to prometheus service
|
||||
* @param queryParams query parameters
|
||||
* @internal
|
||||
*/
|
||||
getMetrics(prometheusPath: string, queryParams: IMetricsReqParams & { query: string }) {
|
||||
protected k8sRequest<T = any>(url: URL, options: OptionsOfJSONResponseBody = {}): Promise<T> {
|
||||
options.timeout ??= 5000;
|
||||
options.headers ??= {};
|
||||
options.headers["content-type"] ??= "application/json";
|
||||
options.headers.host ??= `${this.id}.${this.kubeProxyUrl.host}`; // required in ClusterManager.getClusterForRequest()
|
||||
|
||||
return got<T>(url, options).json();
|
||||
}
|
||||
|
||||
getMetrics(prometheusPath: string, searchParams: Record<string, any>) {
|
||||
const prometheusPrefix = this.preferences.prometheus?.prefix || "";
|
||||
const metricsPath = `/api/v1/namespaces/${prometheusPath}/proxy${prometheusPrefix}/api/v1/query_range`;
|
||||
|
||||
return this.k8sRequest(metricsPath, {
|
||||
return this.k8sRequest(this.getUrlTo(metricsPath), {
|
||||
timeout: 0,
|
||||
resolveWithFullResponse: false,
|
||||
json: true,
|
||||
method: "POST",
|
||||
form: queryParams,
|
||||
form: searchParams,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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 { Cluster } from "./cluster";
|
||||
import type httpProxy from "http-proxy";
|
||||
@ -58,14 +58,14 @@ export class ContextHandler {
|
||||
|
||||
async getPrometheusService(): Promise<PrometheusService> {
|
||||
const providers = this.prometheusProvider ? prometheusProviders.filter(provider => provider.id == this.prometheusProvider) : prometheusProviders;
|
||||
const prometheusPromises: Promise<PrometheusService>[] = providers.map(async (provider: PrometheusProvider): Promise<PrometheusService> => {
|
||||
const apiClient = (await this.cluster.getProxyKubeconfig()).makeApiClient(CoreV1Api);
|
||||
|
||||
return await provider.getPrometheusService(apiClient);
|
||||
});
|
||||
const prometheusPromises = providers.map(provider => (
|
||||
this.cluster.getProxyKubeconfig()
|
||||
.then(kubeConfig => kubeConfig.makeApiClient(CoreV1Api))
|
||||
.then(apiClient => provider.getPrometheusService(apiClient))
|
||||
));
|
||||
const resolvedPrometheusServices = await Promise.all(prometheusPromises);
|
||||
|
||||
return resolvedPrometheusServices.filter(n => n)[0];
|
||||
return resolvedPrometheusServices.find(n => n);
|
||||
}
|
||||
|
||||
async getPrometheusPath(): Promise<string> {
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import yaml from "js-yaml";
|
||||
import { readFile } from "fs-extra";
|
||||
import { promiseExec } from "../promise-exec";
|
||||
import { promiseExec, stdOptimizedPromiseExec } from "../promise-exec";
|
||||
import { helmCli } from "./helm-cli";
|
||||
import { Singleton } from "../../common/utils/singleton";
|
||||
import { customRequestPromise } from "../../common/request";
|
||||
import { defaultGotOptions } from "../../common/got-opts";
|
||||
import orderBy from "lodash/orderBy";
|
||||
import logger from "../logger";
|
||||
import got, { OptionsOfJSONResponseBody } from "got";
|
||||
import { filter } from "lodash";
|
||||
|
||||
export type HelmEnv = Record<string, string> & {
|
||||
HELM_REPOSITORY_CACHE?: string;
|
||||
@ -36,14 +38,13 @@ export class HelmRepoManager extends Singleton {
|
||||
protected initialized: boolean;
|
||||
|
||||
async loadAvailableRepos(): Promise<HelmRepo[]> {
|
||||
const res = await customRequestPromise({
|
||||
uri: "https://github.com/lensapp/artifact-hub-repositories/releases/download/latest/repositories.json",
|
||||
json: true,
|
||||
resolveWithFullResponse: true,
|
||||
timeout: 10000,
|
||||
});
|
||||
const options = {
|
||||
...defaultGotOptions() as OptionsOfJSONResponseBody,
|
||||
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() {
|
||||
@ -60,7 +61,7 @@ export class HelmRepoManager extends Singleton {
|
||||
protected async parseHelmEnv() {
|
||||
const helm = await helmCli.binaryPath();
|
||||
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 env: HelmEnv = {};
|
||||
@ -114,50 +115,41 @@ export class HelmRepoManager extends Singleton {
|
||||
|
||||
public async update() {
|
||||
const helm = await helmCli.binaryPath();
|
||||
const { stdout } = await promiseExec(`"${helm}" repo update`).catch((error) => {
|
||||
return { stdout: error.stdout };
|
||||
});
|
||||
const command = `"${helm}" repo update`;
|
||||
|
||||
return stdout;
|
||||
return stdOptimizedPromiseExec(command);
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
const command = `"${helm}" repo add ${name} ${url}`;
|
||||
|
||||
return stdout;
|
||||
return stdOptimizedPromiseExec(command);
|
||||
}
|
||||
|
||||
public async addСustomRepo(repoAttributes : HelmRepo) {
|
||||
logger.info(`[HELM]: adding repo "${repoAttributes.name}" from ${repoAttributes.url}`);
|
||||
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" : "";
|
||||
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;
|
||||
return stdOptimizedPromiseExec(command);
|
||||
}
|
||||
|
||||
public async removeRepo({ name, url }: HelmRepo): Promise<string> {
|
||||
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);
|
||||
});
|
||||
const command = `"${helm}" repo remove ${name}`;
|
||||
|
||||
return stdout;
|
||||
return stdOptimizedPromiseExec(command);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,15 +1,21 @@
|
||||
import { app, remote } from "electron";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import { promiseExec } from "./promise-exec";
|
||||
import logger from "./logger";
|
||||
import { ensureDir, pathExists } from "fs-extra";
|
||||
import got from "got";
|
||||
import path from "path";
|
||||
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 { customRequest } from "../common/request";
|
||||
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 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}`);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const stream = customRequest({
|
||||
url: this.url,
|
||||
gzip: true,
|
||||
});
|
||||
const file = fs.createWriteStream(this.path);
|
||||
logger.info(`Downloading kubectl ${this.kubectlVersion} from ${this.url} to ${this.path}`);
|
||||
const options: GotStreamFunctionOptions = {
|
||||
...defaultGotOptions(),
|
||||
decompress: true,
|
||||
isStream: true,
|
||||
};
|
||||
|
||||
stream.on("complete", () => {
|
||||
logger.debug("kubectl binary download finished");
|
||||
file.end();
|
||||
});
|
||||
stream.on("error", (error) => {
|
||||
logger.error(error);
|
||||
fs.unlink(this.path, () => {
|
||||
// do nothing
|
||||
});
|
||||
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);
|
||||
});
|
||||
try {
|
||||
// TODO: improve the UI for this to use some sort of loading bar TUI
|
||||
await pipeline(
|
||||
got.stream(this.url, options),
|
||||
fs.createWriteStream(this.path)
|
||||
);
|
||||
} catch (err) {
|
||||
logger.error("Failed to download kubectl:", err);
|
||||
}
|
||||
}
|
||||
|
||||
protected async writeInitScripts() {
|
||||
|
||||
@ -1,17 +1,22 @@
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import request from "request";
|
||||
import { ensureDir, pathExists } from "fs-extra";
|
||||
import fse from "fs-extra";
|
||||
import * as tar from "tar";
|
||||
import { isWindows } from "../common/vars";
|
||||
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 = {
|
||||
version: string;
|
||||
baseDir: string;
|
||||
originalBinaryName: string;
|
||||
newBinaryName?: string;
|
||||
requestOpts?: request.Options;
|
||||
requestOpts?: GotStreamFunctionOptions;
|
||||
};
|
||||
|
||||
export class LensBinary {
|
||||
@ -26,7 +31,7 @@ export class LensBinary {
|
||||
protected platformName: string;
|
||||
protected arch: string;
|
||||
protected originalBinaryName: string;
|
||||
protected requestOpts: request.Options;
|
||||
protected requestOpts: GotStreamFunctionOptions;
|
||||
protected logger: Console | winston.Logger;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -110,88 +115,71 @@ export class LensBinary {
|
||||
}
|
||||
|
||||
protected async checkBinary() {
|
||||
const exists = await pathExists(this.getBinaryPath());
|
||||
|
||||
return exists;
|
||||
return fse.pathExists(this.getBinaryPath());
|
||||
}
|
||||
|
||||
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()}`);
|
||||
} catch (err) {
|
||||
this.logger.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
protected async untarBinary() {
|
||||
return new Promise<void>(resolve => {
|
||||
this.logger.debug(`Extracting ${this.originalBinaryName} binary`);
|
||||
tar.x({
|
||||
this.logger.debug(`Extracting ${this.originalBinaryName} binary`);
|
||||
|
||||
try {
|
||||
await tar.x({
|
||||
file: this.tarPath,
|
||||
cwd: this.dirname
|
||||
}).then((() => {
|
||||
resolve();
|
||||
}));
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
this.logger.error(`failed to extract ${this.originalBinaryName}: `, err);
|
||||
}
|
||||
}
|
||||
|
||||
protected async renameBinary() {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this.logger.debug(`Renaming ${this.originalBinaryName} binary to ${this.binaryName}`);
|
||||
fs.rename(this.getOriginalBinaryPath(), this.getBinaryPath(), (err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
this.logger.debug(`Renaming ${this.originalBinaryName} binary to ${this.binaryName}`);
|
||||
|
||||
return fse.promises.rename(this.getOriginalBinaryPath(), this.getBinaryPath());
|
||||
}
|
||||
|
||||
protected async downloadBinary() {
|
||||
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();
|
||||
|
||||
this.logger.info(`Downloading ${this.originalBinaryName} ${this.binaryVersion} from ${url} to ${binaryPath}`);
|
||||
const requestOpts: request.UriOptions & request.CoreOptions = {
|
||||
uri: url,
|
||||
gzip: true,
|
||||
const requestOpts: GotStreamFunctionOptions = {
|
||||
decompress: true,
|
||||
isStream: true,
|
||||
...this.requestOpts
|
||||
};
|
||||
const stream = request(requestOpts);
|
||||
|
||||
stream.on("complete", () => {
|
||||
this.logger.info(`Download of ${this.originalBinaryName} finished`);
|
||||
file.end();
|
||||
});
|
||||
this.logger.info(`Downloading ${this.originalBinaryName} ${this.binaryVersion} from ${url} to ${binaryPath}`);
|
||||
|
||||
stream.on("error", (error) => {
|
||||
this.logger.error(error);
|
||||
fs.unlink(binaryPath, () => {
|
||||
// do nothing
|
||||
});
|
||||
throw(error);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
try {
|
||||
await pipeline(
|
||||
got.stream(url, requestOpts),
|
||||
fs.createWriteStream(binaryPath),
|
||||
);
|
||||
} catch (err) {
|
||||
this.logger.error("Failed to download kubectl:", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { CoreV1Api } from "@kubernetes/client-node";
|
||||
|
||||
export type PrometheusClusterQuery = {
|
||||
export interface PrometheusClusterQuery {
|
||||
memoryUsage: string;
|
||||
memoryRequests: string;
|
||||
memoryLimits: string;
|
||||
@ -11,18 +11,18 @@ export type PrometheusClusterQuery = {
|
||||
cpuCapacity: string;
|
||||
podUsage: string;
|
||||
podCapacity: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type PrometheusNodeQuery = {
|
||||
export interface PrometheusNodeQuery {
|
||||
memoryUsage: string;
|
||||
memoryCapacity: string;
|
||||
cpuUsage: string;
|
||||
cpuCapacity: string;
|
||||
fsSize: string;
|
||||
fsUsage: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type PrometheusPodQuery = {
|
||||
export interface PrometheusPodQuery {
|
||||
memoryUsage: string;
|
||||
memoryRequests: string;
|
||||
memoryLimits: string;
|
||||
@ -32,25 +32,26 @@ export type PrometheusPodQuery = {
|
||||
fsUsage: string;
|
||||
networkReceive: string;
|
||||
networkTransmit: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type PrometheusPvcQuery = {
|
||||
export interface PrometheusPvcQuery {
|
||||
diskUsage: string;
|
||||
diskCapacity: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type PrometheusIngressQuery = {
|
||||
export interface PrometheusIngressQuery {
|
||||
bytesSentSuccess: string;
|
||||
bytesSentFailure: string;
|
||||
requestDurationSeconds: string;
|
||||
responseDurationSeconds: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type PrometheusQueryOpts = {
|
||||
[key: string]: string | any;
|
||||
};
|
||||
|
||||
export type PrometheusQuery = PrometheusNodeQuery | PrometheusClusterQuery | PrometheusPodQuery | PrometheusPvcQuery | PrometheusIngressQuery;
|
||||
export type PrometheusQueryKey = keyof PrometheusNodeQuery | keyof PrometheusClusterQuery | keyof PrometheusPodQuery | keyof PrometheusPvcQuery | keyof PrometheusIngressQuery;
|
||||
|
||||
export type PrometheusService = {
|
||||
id: string;
|
||||
|
||||
@ -2,3 +2,13 @@ import * as util from "util";
|
||||
import { exec } from "child_process";
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,7 +52,7 @@ class HelmApiRoute extends LensApi {
|
||||
const { cluster, params, payload, response } = request;
|
||||
|
||||
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);
|
||||
} catch (error) {
|
||||
@ -81,7 +81,7 @@ class HelmApiRoute extends LensApi {
|
||||
const result = await helmService.listReleases(cluster, params.namespace);
|
||||
|
||||
this.respondJson(response, result);
|
||||
} catch(error) {
|
||||
} catch (error) {
|
||||
logger.debug(error);
|
||||
this.respondText(response, error, 422);
|
||||
}
|
||||
|
||||
@ -4,10 +4,15 @@ import { LensApi } from "../lens-api";
|
||||
import { Cluster, ClusterMetadataKey } from "../cluster";
|
||||
import { ClusterPrometheusMetadata } from "../../common/cluster-store";
|
||||
import logger from "../logger";
|
||||
import { PrometheusQueryKey } from "../prometheus/provider-registry";
|
||||
|
||||
export type IMetricsQuery = string | string[] | {
|
||||
export interface MetricsQuery {
|
||||
[metricName: string]: string;
|
||||
};
|
||||
}
|
||||
|
||||
function delay(ms: number) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
// This is used for backoff retry tracking.
|
||||
const MAX_ATTEMPTS = 5;
|
||||
@ -29,7 +34,7 @@ async function loadMetrics(promQueries: string[], cluster: Cluster, prometheusPa
|
||||
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));
|
||||
}
|
||||
|
||||
export interface MetricsResult {
|
||||
status?: string;
|
||||
data?: any;
|
||||
}
|
||||
|
||||
class MetricsRoute extends LensApi {
|
||||
async routeMetrics({ response, cluster, payload, query }: LensApiRequest) {
|
||||
const queryParams: IMetricsQuery = Object.fromEntries(query.entries());
|
||||
const queryParams: MetricsQuery = Object.fromEntries(query.entries());
|
||||
const prometheusMetadata: ClusterPrometheusMetadata = {};
|
||||
|
||||
try {
|
||||
@ -72,7 +82,7 @@ class MetricsRoute extends LensApi {
|
||||
this.respondJson(response, data);
|
||||
} else {
|
||||
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 data = Object.fromEntries(Object.keys(payload).map((metricName, i) => [metricName, result[i]]));
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
import moment from "moment";
|
||||
import { apiBase } from "../index";
|
||||
import type { IMetricsQuery } from "../../../main/routes/metrics-route";
|
||||
import type { MetricsQuery } from "../../../main/routes/metrics-route";
|
||||
|
||||
export interface IMetrics {
|
||||
status: string;
|
||||
@ -34,7 +34,7 @@ export interface IMetricsReqParams {
|
||||
}
|
||||
|
||||
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;
|
||||
let { start, end } = reqParams;
|
||||
|
||||
|
||||
@ -13,7 +13,8 @@ export default function (): webpack.Configuration {
|
||||
context: __dirname,
|
||||
target: "electron-main",
|
||||
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,
|
||||
entry: {
|
||||
main: path.resolve(mainDir, "index.ts"),
|
||||
|
||||
129
yarn.lock
129
yarn.lock
@ -972,6 +972,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
|
||||
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":
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.0.tgz#c8d68821a854c555bba172f3b06959a0039b236d"
|
||||
@ -993,10 +998,17 @@
|
||||
dependencies:
|
||||
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":
|
||||
version "7.29.4"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.29.4.tgz#1647c2b478789621ead7a50614ad81ab5ae5b86c"
|
||||
integrity sha512-CtrJRiSYEfbtNGtEsd78mk1n1v2TUbeABlNIcOCJdDfkN5/JTOwQEbbQpoSRxGqzcWPgStMvJ4mNolSuBRv1NA==
|
||||
version "7.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.30.0.tgz#53697851f7708a1448cc30b74a2ea056dd709cd6"
|
||||
integrity sha512-v4GzWtltaiDE0yRikLlcLAfEiiK8+ptu6OuuIebm9GdC2XlZTNDPGEfM2UkEtnH7hr9TRq2sivT5EA9P1Oy7bw==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.10.4"
|
||||
"@babel/runtime" "^7.12.5"
|
||||
@ -1106,6 +1118,16 @@
|
||||
dependencies:
|
||||
"@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@*":
|
||||
version "0.12.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8"
|
||||
@ -1308,6 +1330,11 @@
|
||||
"@types/tapable" "*"
|
||||
"@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@*":
|
||||
version "0.19.3"
|
||||
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"
|
||||
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":
|
||||
version "4.14.155"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.155.tgz#e2b4514f46a261fd11542e47519c20ebce7bc23a"
|
||||
@ -1665,6 +1699,13 @@
|
||||
"@types/tough-cookie" "*"
|
||||
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@*":
|
||||
version "0.12.0"
|
||||
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"
|
||||
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:
|
||||
version "2.1.4"
|
||||
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"
|
||||
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:
|
||||
version "1.0.0"
|
||||
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"
|
||||
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:
|
||||
version "1.1.3"
|
||||
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"
|
||||
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:
|
||||
version "6.7.1"
|
||||
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"
|
||||
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:
|
||||
version "2.0.1"
|
||||
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"
|
||||
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:
|
||||
version "1.0.0"
|
||||
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"
|
||||
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:
|
||||
version "1.0.2"
|
||||
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:
|
||||
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:
|
||||
version "1.0.1"
|
||||
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"
|
||||
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:
|
||||
version "1.0.1"
|
||||
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"
|
||||
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:
|
||||
version "2.0.0"
|
||||
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:
|
||||
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:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user