From 305c4a5573e9e2c8b8768b2515052f54b446061b Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 19 Jan 2023 06:35:33 -0800 Subject: [PATCH] Remove last usages of request in our code (#6911) * Improve the injectability of cluster metadata detection - Remove unnecessary and complex base class Signed-off-by: Sebastian Malton * Remove dead code Signed-off-by: Sebastian Malton * Remove dead code Signed-off-by: Sebastian Malton * Remove last usages of request in our code Signed-off-by: Sebastian Malton * Remove more deps Signed-off-by: Sebastian Malton * Fix tests Signed-off-by: Sebastian Malton * Fix lensFetch Signed-off-by: Sebastian Malton Signed-off-by: Sebastian Malton --- package.json | 5 -- src/common/cluster/cluster.ts | 3 +- ...ns-fetch.global-override-for-injectable.ts | 9 ++++ src/common/fetch/lens-fetch.injectable.ts | 37 ++++++++++++++ src/main/get-metrics.injectable.ts | 14 ++++-- src/main/k8s-request.injectable.ts | 48 ++++++++++++------- yarn.lock | 48 ++++--------------- 7 files changed, 98 insertions(+), 66 deletions(-) create mode 100644 src/common/fetch/lens-fetch.global-override-for-injectable.ts create mode 100644 src/common/fetch/lens-fetch.injectable.ts diff --git a/package.json b/package.json index 15e9b59c2e..d17c26f248 100644 --- a/package.json +++ b/package.json @@ -287,8 +287,6 @@ "react-router": "^5.3.4", "react-virtualized-auto-sizer": "^1.0.7", "readable-stream": "^3.6.0", - "request": "^2.88.2", - "request-promise-native": "^1.0.9", "rfc6902": "^5.0.1", "selfsigned": "^2.1.1", "semver": "^7.3.8", @@ -355,8 +353,6 @@ "@types/react-virtualized-auto-sizer": "^1.0.1", "@types/react-window": "^1.8.5", "@types/readable-stream": "^2.3.13", - "@types/request": "^2.48.7", - "@types/request-promise-native": "^1.0.18", "@types/semver": "^7.3.13", "@types/sharp": "^0.31.1", "@types/tar": "^6.1.3", @@ -461,7 +457,6 @@ "@types/react-router-dom": "^5.3.3", "@types/react-virtualized-auto-sizer": "^1.0.1", "@types/react-window": "^1.8.5", - "@types/request-promise-native": "^1.0.18", "@types/tar": "^6.1.3", "@types/tcp-port-used": "^1.0.1", "@types/url-parse": "^1.4.8", diff --git a/src/common/cluster/cluster.ts b/src/common/cluster/cluster.ts index c769865086..3f353dfee4 100644 --- a/src/common/cluster/cluster.ts +++ b/src/common/cluster/cluster.ts @@ -15,7 +15,6 @@ import plimit from "p-limit"; import type { ClusterState, ClusterMetricsResourceType, ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences, UpdateClusterModel, KubeAuthUpdate, ClusterConfigData } from "../cluster-types"; import { ClusterMetadataKey, initialNodeShellImage, ClusterStatus, clusterModelIdChecker, updateClusterModelChecker } from "../cluster-types"; import { disposer, isDefined, isRequestError, toJS } from "../utils"; -import type { Response } from "request"; import { clusterListNamespaceForbiddenChannel } from "../ipc/cluster"; import type { CanI } from "./authorization-review.injectable"; import type { ListNamespaces } from "./list-namespaces.injectable"; @@ -658,7 +657,7 @@ export class Cluster implements ClusterModel { const namespaceList = [ctx?.namespace].filter(isDefined); if (namespaceList.length === 0 && error instanceof HttpError && error.statusCode === 403) { - const { response } = error as HttpError & { response: Response }; + const { response } = error as HttpError & { response: { body: unknown }}; this.dependencies.logger.info("[CLUSTER]: listing namespaces is forbidden, broadcasting", { clusterId: this.id, error: response.body }); this.dependencies.broadcastMessage(clusterListNamespaceForbiddenChannel, this.id); diff --git a/src/common/fetch/lens-fetch.global-override-for-injectable.ts b/src/common/fetch/lens-fetch.global-override-for-injectable.ts new file mode 100644 index 0000000000..0bc144b1be --- /dev/null +++ b/src/common/fetch/lens-fetch.global-override-for-injectable.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getGlobalOverrideForFunction } from "../test-utils/get-global-override-for-function"; +import lensFetchInjectable from "./lens-fetch.injectable"; + +export default getGlobalOverrideForFunction(lensFetchInjectable); diff --git a/src/common/fetch/lens-fetch.injectable.ts b/src/common/fetch/lens-fetch.injectable.ts new file mode 100644 index 0000000000..a90818e6cb --- /dev/null +++ b/src/common/fetch/lens-fetch.injectable.ts @@ -0,0 +1,37 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { Agent } from "https"; +import type { RequestInit, Response } from "node-fetch"; +import lensProxyPortInjectable from "../../main/lens-proxy/lens-proxy-port.injectable"; +import lensProxyCertificateInjectable from "../certificate/lens-proxy-certificate.injectable"; +import nodeFetchModuleInjectable from "./fetch-module.injectable"; + +export type LensRequestInit = Omit; + +export type LensFetch = (pathnameAndQuery: string, init?: LensRequestInit) => Promise; + +const lensFetchInjectable = getInjectable({ + id: "lens-fetch", + instantiate: (di): LensFetch => { + const { default: fetch } = di.inject(nodeFetchModuleInjectable); + const lensProxyPort = di.inject(lensProxyPortInjectable); + const lensProxyCertificate = di.inject(lensProxyCertificateInjectable); + + return async (pathnameAndQuery, init = {}) => { + const agent = new Agent({ + ca: lensProxyCertificate.get().cert, + }); + + return fetch(`https://127.0.0.1:${lensProxyPort.get()}${pathnameAndQuery}`, { + ...init, + agent, + }); + }; + }, + causesSideEffects: true, +}); + +export default lensFetchInjectable; diff --git a/src/main/get-metrics.injectable.ts b/src/main/get-metrics.injectable.ts index 0ceaf01735..7a17ce3884 100644 --- a/src/main/get-metrics.injectable.ts +++ b/src/main/get-metrics.injectable.ts @@ -4,16 +4,19 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import type { Cluster } from "../common/cluster/cluster"; +import nodeFetchModuleInjectable from "../common/fetch/fetch-module.injectable"; import type { RequestMetricsParams } from "../common/k8s-api/endpoints/metrics.api/request-metrics.injectable"; +import { object } from "../common/utils"; import k8sRequestInjectable from "./k8s-request.injectable"; -export type GetMetrics = (cluster: Cluster, prometheusPath: string, queryParams: RequestMetricsParams & { query: string }) => Promise; +export type GetMetrics = (cluster: Cluster, prometheusPath: string, queryParams: RequestMetricsParams & { query: string }) => Promise; const getMetricsInjectable = getInjectable({ id: "get-metrics", instantiate: (di): GetMetrics => { const k8sRequest = di.inject(k8sRequestInjectable); + const { FormData } = di.inject(nodeFetchModuleInjectable); return async ( cluster, @@ -22,13 +25,16 @@ const getMetricsInjectable = getInjectable({ ) => { const prometheusPrefix = cluster.preferences.prometheus?.prefix || ""; const metricsPath = `/api/v1/namespaces/${prometheusPath}/proxy${prometheusPrefix}/api/v1/query_range`; + const body = new FormData(); + + for (const [key, value] of object.entries(queryParams)) { + body.set(key, value.toString()); + } return k8sRequest(cluster, metricsPath, { timeout: 0, - resolveWithFullResponse: false, - json: true, method: "POST", - form: queryParams, + body, }); }; }, diff --git a/src/main/k8s-request.injectable.ts b/src/main/k8s-request.injectable.ts index c00e70ec3f..b15056b50b 100644 --- a/src/main/k8s-request.injectable.ts +++ b/src/main/k8s-request.injectable.ts @@ -2,35 +2,49 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { RequestPromiseOptions } from "request-promise-native"; -import request from "request-promise-native"; import type { Cluster } from "../common/cluster/cluster"; import { getInjectable } from "@ogre-tools/injectable"; -import lensProxyPortInjectable from "./lens-proxy/lens-proxy-port.injectable"; -import lensProxyCertificateInjectable from "../common/certificate/lens-proxy-certificate.injectable"; +import type { LensRequestInit } from "../common/fetch/lens-fetch.injectable"; +import lensFetchInjectable from "../common/fetch/lens-fetch.injectable"; +import { withTimeout } from "../common/fetch/timeout-controller"; -export type K8sRequest = (cluster: Cluster, path: string, options?: RequestPromiseOptions) => Promise; +export interface K8sRequestInit extends LensRequestInit { + timeout?: number; +} + +export type K8sRequest = (cluster: Cluster, pathnameAndQuery: string, init?: K8sRequestInit) => Promise; const k8sRequestInjectable = getInjectable({ id: "k8s-request", - instantiate: (di) => { - const lensProxyPort = di.inject(lensProxyPortInjectable); - const lensProxyCertificate = di.inject(lensProxyCertificateInjectable); + instantiate: (di): K8sRequest => { + const lensFetch = di.inject(lensFetchInjectable); return async ( - cluster: Cluster, - path: string, - options: RequestPromiseOptions = {}, + cluster, + pathnameAndQuery, + { + timeout = 30_000, + signal, + ...init + } = {}, ) => { - const kubeProxyUrl = `https://127.0.0.1:${lensProxyPort.get()}/${cluster.id}`; + const controller = timeout ? withTimeout(timeout) : undefined; - options.ca = lensProxyCertificate.get().cert; - options.headers ??= {}; - options.json ??= true; - options.timeout ??= 30000; + if (controller) { + signal?.addEventListener("abort", () => controller.abort()); + } - return request(kubeProxyUrl + path, options); + const response = await lensFetch(`/${cluster.id}${pathnameAndQuery}`, { + ...init, + signal: controller?.signal ?? signal, + }); + + if (response.status < 200 || response.status >= 300) { + throw new Error(`Failed to ${init.method ?? "get"} ${pathnameAndQuery} for clusterId=${cluster.id}: ${response.statusText}`, { cause: response }); + } + + return response.json(); }; }, }); diff --git a/yarn.lock b/yarn.lock index 7b6934699b..7a28c3c71b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2619,14 +2619,7 @@ resolved "https://registry.yarnpkg.com/@types/relateurl/-/relateurl-0.2.29.tgz#68ccecec3d4ffdafb9c577fe764f912afc050fe6" integrity sha512-QSvevZ+IRww2ldtfv1QskYsqVVVwCKQf1XbwtcyyoRvLIQzfyPhj/C+3+PKzSDRdiyejaiLgnq//XTkleorpLg== -"@types/request-promise-native@^1.0.18": - version "1.0.18" - resolved "https://registry.yarnpkg.com/@types/request-promise-native/-/request-promise-native-1.0.18.tgz#437ee2d0b772e01c9691a983b558084b4b3efc2c" - integrity sha512-tPnODeISFc/c1LjWyLuZUY+Z0uLB3+IMfNoQyDEi395+j6kTFTTRAqjENjoPJUid4vHRGEozoTrcTrfZM+AcbA== - dependencies: - "@types/request" "*" - -"@types/request@*", "@types/request@^2.47.1", "@types/request@^2.48.7": +"@types/request@^2.47.1": version "2.48.8" resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.8.tgz#0b90fde3b655ab50976cb8c5ac00faca22f5a82c" integrity sha512-whjk1EDJPcAR2kYHRbFl/lKeeKYTi05A15K9bnLInCVroNDCtXce57xKdI0/rQaA3K+6q0eFyUBPmqfSndUZdQ== @@ -11471,23 +11464,7 @@ repeat-string@^1.5.2, repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== -request-promise-core@1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" - integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== - dependencies: - lodash "^4.17.19" - -request-promise-native@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" - integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== - dependencies: - request-promise-core "1.1.4" - stealthy-require "^1.1.1" - tough-cookie "^2.3.3" - -request@^2.88.0, request@^2.88.2: +request@^2.88.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -12346,11 +12323,6 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= -stealthy-require@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" - integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= - stream-buffers@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-3.0.2.tgz#5249005a8d5c2d00b3a32e6e0a6ea209dc4f3521" @@ -12906,14 +12878,6 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -tough-cookie@^2.3.3, tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - tough-cookie@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" @@ -12923,6 +12887,14 @@ tough-cookie@^4.0.0: punycode "^2.1.1" universalify "^0.1.2" +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + tr46@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240"