diff --git a/core/src/plugins/kubernetes/retry.ts b/core/src/plugins/kubernetes/retry.ts index 6bedca2b34..7153aa8334 100644 --- a/core/src/plugins/kubernetes/retry.ts +++ b/core/src/plugins/kubernetes/retry.ts @@ -17,6 +17,7 @@ import type { NodeJSErrnoException } from "../../exceptions.js" import { InternalError, isErrnoException } from "../../exceptions.js" import type { ErrorEvent } from "ws" import dns from "node:dns" +import { trim } from "lodash-es" /** * The flag {@code forceRetry} can be used to avoid {@link shouldRetry} helper call in case if the error code @@ -121,6 +122,12 @@ export function toKubernetesError(err: unknown, context: string): KubernetesErro errorType = "WebsocketError" originalMessage = err.message // The ErrorEvent does not expose the status code other than as part of the error.message + } else if (err instanceof Error && err.name === "Error" && err.cause === undefined) { + // exec auth getCredential function of kubernetes client throws plain error + // see also https://github.com/kubernetes-client/javascript/blob/release-1.x/src/exec_auth.ts + // TODO: fix the client to throw a more recognizable error + errorType = "Error" + originalMessage = trim(err.message) } else { // In all other cases, we don't know what this is, so let's just throw an InternalError throw InternalError.wrapError(err, `toKubernetesError encountered an unknown error during ${context}`) diff --git a/core/test/unit/src/plugins/kubernetes/retry.ts b/core/test/unit/src/plugins/kubernetes/retry.ts index 7ddd0a3484..2035b1e58a 100644 --- a/core/test/unit/src/plugins/kubernetes/retry.ts +++ b/core/test/unit/src/plugins/kubernetes/retry.ts @@ -10,6 +10,7 @@ import type { ErrorEvent, WebSocket } from "ws" import { shouldRetry, toKubernetesError } from "../../../../../src/plugins/kubernetes/retry.js" import { expect } from "chai" import dedent from "dedent" +import { expectError } from "../../../../helpers.js" const testKubeOp = "test" const websocketError: ErrorEvent = { @@ -18,6 +19,8 @@ const websocketError: ErrorEvent = { type: "error", target: true as unknown as WebSocket, } +const plainError = new Error("failed to refresh token") +const syntaxError = new SyntaxError("invalid syntax") describe("toKubernetesError", () => { it("should handle WebsocketError", () => { @@ -33,6 +36,26 @@ describe("toKubernetesError", () => { expect(err.apiMessage).to.be.undefined expect(err.type).to.equal("kubernetes") }) + + it("should handle plain error gracefully", () => { + const err = toKubernetesError(plainError, testKubeOp) + + expect(err).to.be.instanceof(KubernetesError) + expect(err.message).to.equal(dedent` + Error while performing Kubernetes API operation test: Error + + failed to refresh token + `) + expect(err.responseStatusCode).to.be.undefined + expect(err.apiMessage).to.be.undefined + expect(err.type).to.equal("kubernetes") + }) + + it("should crash on other errors like TypeError and SyntaxError", async () => { + await expectError(async () => toKubernetesError(syntaxError, testKubeOp), { + type: "crash", + }) + }) }) describe("shouldRetry", () => {