Upgrade to latest node-fetch (#6046)

* Upgrade to latest node-fetch

- Introduce injection tokens for retriving the implementation of
  node-fetch via await import() calls

- Add webpack file for compiling node-fetch to electron-renderer single
  file format

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Update lock file

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add type packages which were removed for some reason

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add more dev @types deps that weren't added last time

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Simpify by using webpack to create a commonjs package

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix build for integration tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix unit tests on CI not having all deps

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Revert accidental timeout change

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Replace manually specifying nodeJS externals with preset

Signed-off-by: Sebastian Malton <sebastian@malton.name>

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton authored and GitHub committed 2022-11-14 10:46:53 -05:00
1 parent 6d9a76835c
commit 86ac417cab
20 files changed
+371 -237

No files matched your search

+1 -2
View File
@@ -4,7 +4,6 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { RequestInit, Response } from "node-fetch";
import type { JsonValue } from "type-fest";
import type { AsyncResult } from "../utils/async-result";
import fetchInjectable from "./fetch.injectable";
@@ -12,7 +11,7 @@ export interface DownloadJsonOptions {
signal?: AbortSignal | null | undefined;
}
export type DownloadJson = (url: string, opts?: DownloadJsonOptions) => Promise<AsyncResult<JsonValue, string>>;
export type DownloadJson = (url: string, opts?: DownloadJsonOptions) => Promise<AsyncResult<unknown, string>>;
const downloadJsonInjectable = getInjectable({
id: "download-json",
@@ -3,9 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getGlobalOverride } from "../test-utils/get-global-override";
import { getGlobalOverrideForFunction } from "../test-utils/get-global-override-for-function";
import fetchInjectable from "./fetch.injectable";
export default getGlobalOverride(fetchInjectable, () => () => {
throw new Error("tried to fetch a resource without override in test");
});
export default getGlobalOverrideForFunction(fetchInjectable);
+6 -2
View File
@@ -3,8 +3,12 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import fetch from "node-fetch";
import type { RequestInit, Response } from "node-fetch";
import type * as FetchModule from "node-fetch";
const { NodeFetch: { default: fetch }} = require("../../../build/webpack/node-fetch.bundle") as { NodeFetch: typeof FetchModule };
type Response = FetchModule.Response;
type RequestInit = FetchModule.RequestInit;
export type Fetch = (url: string, init?: RequestInit) => Promise<Response>;
+89 -33
View File
@@ -15,20 +15,76 @@ import type { Fetch } from "../../fetch/fetch.injectable";
import fetchInjectable from "../../fetch/fetch.injectable";
import type { CreateKubeApiForRemoteCluster } from "../create-kube-api-for-remote-cluster.injectable";
import createKubeApiForRemoteClusterInjectable from "../create-kube-api-for-remote-cluster.injectable";
import { Response } from "node-fetch";
import type { AsyncFnMock } from "@async-fn/jest";
import asyncFn from "@async-fn/jest";
import { flushPromises } from "../../test-utils/flush-promises";
import createKubeJsonApiInjectable from "../create-kube-json-api.injectable";
import type { IKubeWatchEvent } from "../kube-watch-event";
import type { KubeJsonApiDataFor } from "../kube-object";
import type { Response, Headers as NodeFetchHeaders } from "node-fetch";
import AbortController from "abort-controller";
const createMockResponseFromString = (url: string, data: string, statusCode = 200) => {
const res: jest.Mocked<Response> = {
buffer: jest.fn(async () => { throw new Error("buffer() is not supported"); }),
clone: jest.fn(() => res),
arrayBuffer: jest.fn(async () => { throw new Error("arrayBuffer() is not supported"); }),
blob: jest.fn(async () => { throw new Error("blob() is not supported"); }),
body: new PassThrough(),
bodyUsed: false,
headers: new Headers() as NodeFetchHeaders,
json: jest.fn(async () => JSON.parse(await res.text())),
ok: 200 <= statusCode && statusCode < 300,
redirected: 300 <= statusCode && statusCode < 400,
size: data.length,
status: statusCode,
statusText: "some-text",
text: jest.fn(async () => data),
type: "basic",
url,
formData: jest.fn(async () => { throw new Error("formData() is not supported"); }),
};
return res;
};
const createMockResponseFromStream = (url: string, stream: NodeJS.ReadableStream, statusCode = 200) => {
const res: jest.Mocked<Response> = {
buffer: jest.fn(async () => { throw new Error("buffer() is not supported"); }),
clone: jest.fn(() => res),
arrayBuffer: jest.fn(async () => { throw new Error("arrayBuffer() is not supported"); }),
blob: jest.fn(async () => { throw new Error("blob() is not supported"); }),
body: stream,
bodyUsed: false,
headers: new Headers() as NodeFetchHeaders,
json: jest.fn(async () => JSON.parse(await res.text())),
ok: 200 <= statusCode && statusCode < 300,
redirected: 300 <= statusCode && statusCode < 400,
size: 10,
status: statusCode,
statusText: "some-text",
text: jest.fn(() => {
const chunks: Buffer[] = [];
return new Promise((resolve, reject) => {
stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
stream.on("error", (err) => reject(err));
stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
});
}),
type: "basic",
url,
formData: jest.fn(async () => { throw new Error("formData() is not supported"); }),
};
return res;
};
describe("createKubeApiForRemoteCluster", () => {
let createKubeApiForRemoteCluster: CreateKubeApiForRemoteCluster;
let fetchMock: AsyncFnMock<Fetch>;
beforeEach(() => {
beforeEach(async () => {
const di = getDiForUnitTesting({ doGeneralOverrides: true });
fetchMock = asyncFn();
@@ -94,7 +150,7 @@ describe("createKubeApiForRemoteCluster", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["https://127.0.0.1:6443/api/v1/pods"],
new Response(JSON.stringify({
createMockResponseFromString("https://127.0.0.1:6443/api/v1/pods", JSON.stringify({
kind: "PodList",
apiVersion: "v1",
metadata:{
@@ -118,7 +174,7 @@ describe("KubeApi", () => {
let registerApiSpy: jest.SpiedFunction<ApiManager["registerApi"]>;
let fetchMock: AsyncFnMock<Fetch>;
beforeEach(() => {
beforeEach(async () => {
const di = getDiForUnitTesting({ doGeneralOverrides: true });
fetchMock = asyncFn();
@@ -172,7 +228,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1"],
new Response(JSON.stringify({
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1", JSON.stringify({
resources: [{
name: "ingresses",
}],
@@ -196,7 +252,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io"],
new Response(JSON.stringify({
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io", JSON.stringify({
preferredVersion: {
version: "v1",
},
@@ -234,7 +290,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo"],
new Response(JSON.stringify({})),
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo", JSON.stringify({})),
);
result = await getCall;
});
@@ -274,7 +330,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1"],
new Response(JSON.stringify({})),
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1", JSON.stringify({})),
);
result = await getCall;
});
@@ -292,7 +348,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo"],
new Response(JSON.stringify({
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo", JSON.stringify({
apiVersion: "v1",
kind: "Ingress",
metadata: {
@@ -341,7 +397,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1"],
new Response(JSON.stringify({})),
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1", JSON.stringify({})),
);
result = await getCall;
});
@@ -359,7 +415,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1"],
new Response(JSON.stringify({
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1", JSON.stringify({
resources: [],
})),
);
@@ -381,7 +437,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1"],
new Response(JSON.stringify({
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1", JSON.stringify({
resources: [{
name: "ingresses",
}],
@@ -405,7 +461,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/extensions"],
new Response(JSON.stringify({
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/extensions", JSON.stringify({
preferredVersion: {
version: "v1beta1",
},
@@ -443,7 +499,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo"],
new Response(JSON.stringify({})),
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo", JSON.stringify({})),
);
result = await getCall;
});
@@ -483,7 +539,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1"],
new Response(JSON.stringify({})),
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1", JSON.stringify({})),
);
result = await getCall;
});
@@ -501,7 +557,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo"],
new Response(JSON.stringify({
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo", JSON.stringify({
apiVersion: "v1beta1",
kind: "Ingress",
metadata: {
@@ -550,7 +606,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1"],
new Response(JSON.stringify({})),
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1", JSON.stringify({})),
);
result = await getCall;
});
@@ -604,7 +660,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/apps/v1/namespaces/default/deployments/test"],
new Response(JSON.stringify({
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/apps/v1/namespaces/default/deployments/test", JSON.stringify({
apiVersion: "v1",
kind: "Deployment",
metadata: {
@@ -657,7 +713,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/apps/v1/namespaces/default/deployments/test"],
new Response(JSON.stringify({
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/apps/v1/namespaces/default/deployments/test", JSON.stringify({
apiVersion: "v1",
kind: "Deployment",
metadata: {
@@ -710,7 +766,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/apps/v1/namespaces/default/deployments/test"],
new Response(JSON.stringify({
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/apps/v1/namespaces/default/deployments/test", JSON.stringify({
apiVersion: "v1",
kind: "Deployment",
metadata: {
@@ -768,7 +824,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods/foo?propagationPolicy=Background"],
new Response("{}"),
createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods/foo?propagationPolicy=Background", "{}"),
);
});
@@ -804,7 +860,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods/foo?propagationPolicy=Background"],
new Response("{}"),
createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods/foo?propagationPolicy=Background", "{}"),
);
});
@@ -840,7 +896,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/api/v1/namespaces/test/pods/foo?propagationPolicy=Background"],
new Response("{}"),
createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/namespaces/test/pods/foo?propagationPolicy=Background", "{}"),
);
});
@@ -886,7 +942,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/api/v1/namespaces/foo?propagationPolicy=Background"],
new Response("{}"),
createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/namespaces/foo?propagationPolicy=Background", "{}"),
);
});
@@ -922,7 +978,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/api/v1/namespaces/foo?propagationPolicy=Background"],
new Response("{}"),
createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/namespaces/foo?propagationPolicy=Background", "{}"),
);
});
@@ -995,7 +1051,7 @@ describe("KubeApi", () => {
return isMatch;
},
new Response(stream),
createMockResponseFromStream("http://127.0.0.1:9999/api-kube/api/v1/namespaces/kube-system/pods?watch=1&resourceVersion=", stream),
);
});
@@ -1091,7 +1147,7 @@ describe("KubeApi", () => {
return isMatch;
},
new Response(stream),
createMockResponseFromStream("http://127.0.0.1:9999/api-kube/api/v1/namespaces/kube-system/pods?watch=1&resourceVersion=", stream),
);
});
@@ -1186,7 +1242,7 @@ describe("KubeApi", () => {
return isMatch;
},
new Response(stream),
createMockResponseFromStream("http://127.0.0.1:9999/api-kube/api/v1/namespaces/kube-system/pods?watch=1&resourceVersion=&timeoutSeconds=60", stream),
);
});
@@ -1350,7 +1406,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods"],
new Response(JSON.stringify({
createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods", JSON.stringify({
kind: "Pod",
apiVersion: "v1",
metadata: {
@@ -1462,7 +1518,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods/foobar"],
new Response(JSON.stringify({
createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods/foobar", JSON.stringify({
kind: "Pod",
apiVersion: "v1",
metadata: {
@@ -1530,7 +1586,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/api/v1/pods"],
new Response(JSON.stringify({
createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/pods", JSON.stringify({
kind: "PodList",
apiVersion: "v1",
metadata: {},
@@ -1572,7 +1628,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/api/v1/pods"],
new Response(JSON.stringify({
createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/pods", JSON.stringify({
kind: "PodList",
apiVersion: "v1",
metadata: {},
@@ -1614,7 +1670,7 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods"],
new Response(JSON.stringify({
createMockResponseFromString("http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods", JSON.stringify({
kind: "PodList",
apiVersion: "v1",
metadata: {},
+17 -4
View File
@@ -14,7 +14,7 @@ import byline from "byline";
import type { IKubeWatchEvent } from "./kube-watch-event";
import type { KubeJsonApiData, KubeJsonApi } from "./kube-json-api";
import type { Disposer } from "../utils";
import { isDefined, noop, WrappedAbortController } from "../utils";
import { setTimeoutFor, isDefined, noop, WrappedAbortController } from "../utils";
import type { RequestInit, Response } from "node-fetch";
import type { Patch } from "rfc6902";
import assert from "assert";
@@ -643,11 +643,12 @@ export class KubeApi<
clearTimeout(timedRetry);
});
setTimeoutFor(abortController, 600 * 1000);
const requestParams = timeout ? { query: { timeoutSeconds: timeout }} : {};
const watchUrl = this.getWatchUrl(namespace);
const responsePromise = this.request.getResponse(watchUrl, requestParams, {
signal: abortController.signal,
timeout: 600_000,
});
logger.info(`[KUBE-API] watch (${watchId}) ${retry === true ? "retried" : "started"} ${watchUrl}`);
@@ -686,7 +687,19 @@ export class KubeApi<
}, timeout * 1000 * 1.1);
}
["end", "close", "error"].forEach((eventName) => {
if (!response.body) {
logger.error(`[KUBE-API]: watch (${watchId}) did not return a body`);
requestRetried = true;
clearTimeout(timedRetry);
timedRetry = setTimeout(() => { // we did not get any kubernetes errors so let's retry
this.watch({ ...opts, namespace, callback, watchId, retry: true });
}, 1000);
return;
}
for (const eventName of ["end", "close", "error"]) {
response.body.on(eventName, () => {
// We only retry if we haven't retried, haven't aborted and haven't received k8s error
// kubernetes errors (=errorReceived set) should be handled in a callback
@@ -703,7 +716,7 @@ export class KubeApi<
this.watch({ ...opts, namespace, callback, watchId, retry: true });
}, 1000);
});
});
}
byline(response.body).on("data", (line) => {
try {
-1
View File
@@ -473,7 +473,6 @@ export abstract class KubeObjectStore<
callback,
});
// TODO: upgrade node-fetch once we are starting to use ES modules
const signal = abortController.signal;
const callback: KubeApiWatchCallback<D> = (data, error) => {
+6
View File
@@ -18,3 +18,9 @@ export class WrappedAbortController extends AbortController {
});
}
}
export function setTimeoutFor(controller: AbortController, timeout: number): void {
const handle = setTimeout(() => controller.abort(), timeout);
controller.signal.addEventListener("abort", () => clearTimeout(handle));
}
@@ -11,9 +11,9 @@ const lensResourcesDirInjectable = getInjectable({
instantiate: (di) => {
const isProduction = di.inject(isProductionInjectable);
return !isProduction
? process.cwd()
: process.resourcesPath;
return isProduction
? process.resourcesPath
: process.cwd();
},
causesSideEffects: true,