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

fix changing to got

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2021-03-10 14:29:19 -05:00
parent 0b98f59f59
commit 07b75b8027
14 changed files with 70 additions and 97 deletions

View File

@ -234,8 +234,6 @@
"react-dom": "^17.0.1", "react-dom": "^17.0.1",
"react-router": "^5.2.0", "react-router": "^5.2.0",
"readable-stream": "^3.6.0", "readable-stream": "^3.6.0",
"request": "^2.88.2",
"request-promise-native": "^1.0.8",
"semver": "^7.3.2", "semver": "^7.3.2",
"serializr": "^2.0.3", "serializr": "^2.0.3",
"shell-env": "^3.0.1", "shell-env": "^3.0.1",
@ -291,8 +289,6 @@
"@types/react-select": "^3.0.13", "@types/react-select": "^3.0.13",
"@types/react-window": "^1.8.2", "@types/react-window": "^1.8.2",
"@types/readable-stream": "^2.3.9", "@types/readable-stream": "^2.3.9",
"@types/request": "^2.48.5",
"@types/request-promise-native": "^1.0.17",
"@types/semver": "^7.2.0", "@types/semver": "^7.2.0",
"@types/sharp": "^0.26.0", "@types/sharp": "^0.26.0",
"@types/shelljs": "^0.8.8", "@types/shelljs": "^0.8.8",

View File

@ -1,4 +1,4 @@
import requestPromise from "request-promise-native"; import got from "got/dist/source";
import packageInfo from "../../../package.json"; import packageInfo from "../../../package.json";
export function getAppVersion(): string { export function getAppVersion(): string {
@ -14,11 +14,7 @@ export function getBundledExtensions(): string[] {
} }
export async function getAppVersionFromProxyServer(proxyPort: number): Promise<string> { export async function getAppVersionFromProxyServer(proxyPort: number): Promise<string> {
const response = await requestPromise({ const responseJson = await got(`http://localhost:${proxyPort}/version`).json<any>();
method: "GET",
uri: `http://localhost:${proxyPort}/version`,
resolveWithFullResponse: true
});
return JSON.parse(response.body).version; return responseJson.version;
} }

View File

@ -1,4 +1,4 @@
import request from "request"; import got, { CancelableRequest } from "got";
export interface DownloadFileOptions { export interface DownloadFileOptions {
url: string; url: string;
@ -6,32 +6,6 @@ export interface DownloadFileOptions {
timeout?: number; timeout?: number;
} }
export interface DownloadFileTicket { export function downloadFile({ url, timeout, gzip = true }: DownloadFileOptions): CancelableRequest<Buffer> {
url: string; return got(url, { timeout, decompress: gzip }).buffer();
promise: Promise<Buffer>;
cancel(): void;
}
export function downloadFile({ url, timeout, gzip = true }: DownloadFileOptions): DownloadFileTicket {
const fileChunks: Buffer[] = [];
const req = request(url, { gzip, timeout });
const promise: Promise<Buffer> = new Promise((resolve, reject) => {
req.on("data", (chunk: Buffer) => {
fileChunks.push(chunk);
});
req.once("error", err => {
reject({ url, err });
});
req.once("complete", () => {
resolve(Buffer.concat(fileChunks));
});
});
return {
url,
promise,
cancel() {
req.abort();
}
};
} }

View File

@ -1,5 +1,5 @@
import request, { RequestPromiseOptions } from "request-promise-native"; import { OptionsOfJSONResponseBody, Response } from "got";
import { Cluster } from "../cluster"; import { Cluster, k8sRequest } from "../cluster";
export type ClusterDetectionResult = { export type ClusterDetectionResult = {
value: string | number | boolean value: string | number | boolean
@ -18,17 +18,10 @@ export class BaseClusterDetector {
return null; return null;
} }
protected async k8sRequest<T = any>(path: string, options: RequestPromiseOptions = {}): Promise<T> { protected async k8sRequest<T>(path: string, options: OptionsOfJSONResponseBody = {}): Promise<[Response<T>, T]> {
const apiUrl = this.cluster.kubeProxyUrl + path; options.headers ??= {};
options.headers["Host"] ??= `${this.cluster.id}.${this.cluster.kubeProxyUrl.host}`; // required in ClusterManager.getClusterForRequest()''
return request(apiUrl, { return k8sRequest(this.cluster.getUrlTo(path), options);
json: true,
timeout: 30000,
...options,
headers: {
Host: `${this.cluster.id}.${this.cluster.kubeProxyUrl.host}`, // required in ClusterManager.getClusterForRequest()
...(options.headers || {}),
},
});
} }
} }

View File

@ -19,8 +19,8 @@ export class ClusterIdDetector extends BaseClusterDetector {
} }
protected async getDefaultNamespaceId() { protected async getDefaultNamespaceId() {
const response = await this.k8sRequest("/api/v1/namespaces/default"); const [, responseJson] = await this.k8sRequest<any>("/api/v1/namespaces/default");
return response.metadata.uid; return responseJson.metadata.uid;
} }
} }

View File

@ -90,9 +90,9 @@ export class DistributionDetector extends BaseClusterDetector {
public async getKubernetesVersion() { public async getKubernetesVersion() {
if (this.cluster.version) return this.cluster.version; if (this.cluster.version) return this.cluster.version;
const response = await this.k8sRequest("/version"); const [,responseJson] = await this.k8sRequest<any>("/version");
return response.gitVersion; return responseJson.gitVersion;
} }
protected isGKE() { protected isGKE() {
@ -169,9 +169,9 @@ export class DistributionDetector extends BaseClusterDetector {
protected async isOpenshift() { protected async isOpenshift() {
try { try {
const response = await this.k8sRequest(""); const [, responseJson] = await this.k8sRequest<any>("");
return response.paths?.includes("/apis/project.openshift.io"); return responseJson.paths?.includes("/apis/project.openshift.io");
} catch (e) { } catch (e) {
return false; return false;
} }

View File

@ -7,7 +7,7 @@ export class LastSeenDetector extends BaseClusterDetector {
public async detect() { public async detect() {
if (!this.cluster.accessible) return null; if (!this.cluster.accessible) return null;
await this.k8sRequest("/version"); await this.k8sRequest<any>("/version");
return { value: new Date().toJSON(), accuracy: 100 }; return { value: new Date().toJSON(), accuracy: 100 };
} }

View File

@ -12,8 +12,8 @@ export class NodesCountDetector extends BaseClusterDetector {
} }
protected async getNodeCount(): Promise<number> { protected async getNodeCount(): Promise<number> {
const response = await this.k8sRequest("/api/v1/nodes"); const [, responseJson] = await this.k8sRequest<any>("/api/v1/nodes");
return response.items.length; return responseJson.items.length;
} }
} }

View File

@ -12,8 +12,8 @@ export class VersionDetector extends BaseClusterDetector {
} }
public async getKubernetesVersion() { public async getKubernetesVersion() {
const response = await this.k8sRequest("/version"); const [, responseJson] = await this.k8sRequest<any>("/version", { retry: 0 });
return response.gitVersion; return responseJson.gitVersion;
} }
} }

View File

@ -14,7 +14,7 @@ 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 got, { OptionsOfJSONResponseBody, Response } from "got";
import { URL } from "url"; import { URL } from "url";
export enum ClusterStatus { export enum ClusterStatus {
@ -51,6 +51,24 @@ export interface ClusterState {
isGlobalWatchEnabled: boolean; isGlobalWatchEnabled: boolean;
} }
export async function k8sRequest<T = any>(url: string | URL, options: OptionsOfJSONResponseBody = {}): Promise<[Response<T>, T]> {
options.timeout ??= 5000;
options.headers ??= {};
options.headers["content-type"] ??= "application/json";
options.throwHttpErrors ??= true;
try {
const resp = got<T>(url, options);
const jsonResp = resp.json<T>();
return await Promise.all([resp, jsonResp]);
} catch(error) {
logger.error(`[REQUEST]: failed to get ${url}`, { error });
throw error;
}
}
/** /**
* Cluster * Cluster
* *
@ -495,7 +513,7 @@ export class Cluster implements ClusterModel, ClusterState {
return this.kubeconfigManager.getPath(); return this.kubeconfigManager.getPath();
} }
private getUrlTo(path: string): URL { public getUrlTo(path: string): URL {
const url = new URL(this.kubeProxyUrl.toString()); const url = new URL(this.kubeProxyUrl.toString());
url.pathname += path; url.pathname += path;
@ -538,34 +556,36 @@ export class Cluster implements ClusterModel, ClusterState {
const versionData = await versionDetector.detect(); const versionData = await versionDetector.detect();
this.metadata.version = versionData.value; this.metadata.version = versionData.value;
this.failureReason = null; this.failureReason = null;
return ClusterStatus.AccessGranted; return ClusterStatus.AccessGranted;
} catch (error) { } catch (error) {
logger.error(`Failed to connect cluster "${this.contextName}": ${error}`); logger.error(`Failed to connect cluster "${this.contextName}"`, { error });
if (error instanceof got.TimeoutError) {
this.failureReason = "Connection timed out";
return ClusterStatus.Offline;
}
if (error.statusCode) { if (error.statusCode) {
if (error.statusCode >= 400 && error.statusCode < 500) { if (error.statusCode >= 400 && error.statusCode < 500) {
this.failureReason = "Invalid credentials"; this.failureReason = "Invalid credentials";
return ClusterStatus.AccessDenied; return ClusterStatus.AccessDenied;
} else {
this.failureReason = error.error || error.message;
return ClusterStatus.Offline;
} }
} else if (error.failed === true) {
if (error.timedOut === true) {
this.failureReason = "Connection timed out";
return ClusterStatus.Offline; this.failureReason = error.error || error.message;
} else {
this.failureReason = "Failed to fetch credentials";
return ClusterStatus.AccessDenied; return ClusterStatus.Offline;
}
} }
if (error.failed === true) {
this.failureReason = "Failed to fetch credentials";
return ClusterStatus.AccessDenied;
}
this.failureReason = error.message; this.failureReason = error.message;
return ClusterStatus.Offline; return ClusterStatus.Offline;

View File

@ -12,15 +12,17 @@ export interface MetricsQuery {
// prometheus metrics loader // prometheus metrics loader
async function loadMetrics(promQueries: string[], cluster: Cluster, prometheusPath: string, queryParams: Record<string, string>): Promise<any[]> { async function loadMetrics(promQueries: string[], cluster: Cluster, prometheusPath: string, queryParams: Record<string, string>): Promise<any[]> {
const queries = promQueries.map(p => p.trim()); const queries = promQueries.map(p => p.trim());
const loaders = new Map<string, Promise<any>>(); const loaders = new Map<string, ReturnType<(typeof cluster)["getMetrics"]>>();
async function loadMetric(query: string): Promise<any> { async function loadMetric(query: string) {
const searchParams = { query, ...queryParams }; const searchParams = { query, ...queryParams };
return loaders.get(query) ?? loaders.set(query, cluster.getMetrics(prometheusPath, { searchParams, retry: 5 })).get(query); return loaders.get(query) ?? loaders.set(query, cluster.getMetrics(prometheusPath, { searchParams, retry: 5 })).get(query);
} }
return Promise.all(queries.map(loadMetric)); const responses = await Promise.all(queries.map(loadMetric));
return responses.map(([, respJson]) => respJson);
} }
export interface MetricsResult { export interface MetricsResult {
@ -69,7 +71,7 @@ class MetricsRoute extends LensApi {
} }
prometheusMetadata.success = true; prometheusMetadata.success = true;
} catch (error) { } catch (error) {
logger.error("[METRICS]: failed to load metrics", { error }); logger.error(`[METRICS]: failed to load metrics: ${error}`, { error });
prometheusMetadata.success = false; prometheusMetadata.success = false;
this.respondJson(response, {}); this.respondJson(response, {});

View File

@ -170,8 +170,7 @@ export class Extensions extends React.Component {
// install via url // install via url
// fixme: improve error messages for non-tar-file URLs // fixme: improve error messages for non-tar-file URLs
if (InputValidators.isUrl.validate(installPath)) { if (InputValidators.isUrl.validate(installPath)) {
const { promise: filePromise } = downloadFile({ url: installPath, timeout: 60000 /*1m*/ }); const data = await downloadFile({ url: installPath, timeout: 60000 /*1m*/ });
const data = await filePromise;
await this.requestInstall({ fileName, data }); await this.requestInstall({ fileName, data });
} }

View File

@ -44,7 +44,7 @@ export class ClustersMenu extends React.Component<Props> {
click: actions.showSettings click: actions.showSettings
})); }));
if (cluster.online) { if (cluster.online || cluster.failureReason) {
menu.append(new MenuItem({ menu.append(new MenuItem({
label: `Disconnect`, label: `Disconnect`,
click: actions.disconnect click: actions.disconnect

View File

@ -1682,14 +1682,7 @@
resolved "https://registry.yarnpkg.com/@types/relateurl/-/relateurl-0.2.28.tgz#6bda7db8653fa62643f5ee69e9f69c11a392e3a6" resolved "https://registry.yarnpkg.com/@types/relateurl/-/relateurl-0.2.28.tgz#6bda7db8653fa62643f5ee69e9f69c11a392e3a6"
integrity sha1-a9p9uGU/piZD9e5p6facEaOS46Y= integrity sha1-a9p9uGU/piZD9e5p6facEaOS46Y=
"@types/request-promise-native@^1.0.17": "@types/request@^2.47.1":
version "1.0.17"
resolved "https://registry.yarnpkg.com/@types/request-promise-native/-/request-promise-native-1.0.17.tgz#74a2d7269aebf18b9bdf35f01459cf0a7bfc7fab"
integrity sha512-05/d0WbmuwjtGMYEdHIBZ0tqMJJQ2AD9LG2F6rKNBGX1SSFR27XveajH//2N/XYtual8T9Axwl+4v7oBtPUZqg==
dependencies:
"@types/request" "*"
"@types/request@*", "@types/request@^2.47.1", "@types/request@^2.48.5":
version "2.48.5" version "2.48.5"
resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.5.tgz#019b8536b402069f6d11bee1b2c03e7f232937a0" resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.5.tgz#019b8536b402069f6d11bee1b2c03e7f232937a0"
integrity sha512-/LO7xRVnL3DxJ1WkPGDQrp4VTV1reX9RkC85mJ+Qzykj2Bdw+mG15aAfDahc76HtknjzE16SX/Yddn6MxVbmGQ== integrity sha512-/LO7xRVnL3DxJ1WkPGDQrp4VTV1reX9RkC85mJ+Qzykj2Bdw+mG15aAfDahc76HtknjzE16SX/Yddn6MxVbmGQ==