mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Add process of cleaning up lens proxy startup if down quickly
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
b86bc6d84c
commit
4c1d07693f
@ -22,6 +22,10 @@ import type { GetClusterForRequest } from "../cluster/get-cluster-for-request.in
|
||||
export type ServerIncomingMessage = SetRequired<http.IncomingMessage, "url" | "method">;
|
||||
export type ProxyRequestHandler = (args: ProxyApiRequestArgs) => void | Promise<void>;
|
||||
|
||||
export interface ListenOptions {
|
||||
signal: AbortSignal;
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
getClusterForRequest: GetClusterForRequest;
|
||||
shellApiRequest: ProxyRequestHandler;
|
||||
@ -108,10 +112,14 @@ export class LensProxy {
|
||||
*
|
||||
* Resolves with the port number that was picked
|
||||
*/
|
||||
private attemptToListen(): Promise<number> {
|
||||
private attemptToListen(options: ListenOptions): Promise<number> {
|
||||
return new Promise<number>((resolve, reject) => {
|
||||
this.proxyServer.listen(0, "127.0.0.1");
|
||||
|
||||
options.signal.addEventListener("abort", () => {
|
||||
this.close();
|
||||
});
|
||||
|
||||
this.proxyServer
|
||||
.once("listening", () => {
|
||||
this.proxyServer.removeAllListeners("error"); // don't reject the promise
|
||||
@ -140,12 +148,12 @@ export class LensProxy {
|
||||
* @resolves After the server is listening on a good port
|
||||
* @rejects if there is an error before that happens
|
||||
*/
|
||||
async listen(): Promise<void> {
|
||||
async listen(options: ListenOptions): Promise<void> {
|
||||
const seenPorts = new Set<number>();
|
||||
|
||||
while(true) {
|
||||
this.proxyServer?.close();
|
||||
const port = await this.attemptToListen();
|
||||
const port = await this.attemptToListen(options);
|
||||
|
||||
if (!disallowedPorts.has(port)) {
|
||||
// We didn't get a port that would result in an ERR_UNSAFE_PORT error, use it
|
||||
|
||||
@ -4,16 +4,31 @@
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import lensFetchInjectable from "../../../../common/fetch/lens-fetch.injectable";
|
||||
import type { AbortSignal as NonStandardAbortSignal } from "abort-controller";
|
||||
|
||||
export interface RequestOptions {
|
||||
signal: AbortSignal;
|
||||
}
|
||||
|
||||
export type RequestAppVersionViaProxy = (options: RequestOptions) => Promise<string>;
|
||||
|
||||
const requestAppVersionViaProxyInjectable = getInjectable({
|
||||
id: "request-app-version-via-proxy",
|
||||
instantiate: (di) => {
|
||||
instantiate: (di): RequestAppVersionViaProxy => {
|
||||
const lensFetch = di.inject(lensFetchInjectable);
|
||||
|
||||
return async () => {
|
||||
const response = await lensFetch("/version");
|
||||
return async (options) => {
|
||||
const response = await lensFetch("/version", {
|
||||
signal: options.signal as NonStandardAbortSignal,
|
||||
});
|
||||
|
||||
return (await response.json() as { version: string }).version;
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Retrieving version failed: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const body = await response.json() as { version: string };
|
||||
|
||||
return body.version;
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@ -3,68 +3,19 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import exitAppInjectable from "../../../electron-app/features/exit-app.injectable";
|
||||
import lensProxyInjectable from "../../../lens-proxy/lens-proxy.injectable";
|
||||
import loggerInjectable from "../../../../common/logger.injectable";
|
||||
import isWindowsInjectable from "../../../../common/vars/is-windows.injectable";
|
||||
import showErrorPopupInjectable from "../../../electron-app/features/show-error-popup.injectable";
|
||||
import { beforeApplicationIsLoadingInjectionToken } from "../../runnable-tokens/before-application-is-loading-injection-token";
|
||||
import buildVersionInjectable from "../../../vars/build-version/build-version.injectable";
|
||||
import requestAppVersionViaProxyInjectable from "./request-app-version.injectable";
|
||||
import setupLensProxyStartableStoppableInjectable from "./startable-stoppable.injectable";
|
||||
|
||||
const setupLensProxyInjectable = getInjectable({
|
||||
id: "setup-lens-proxy",
|
||||
|
||||
instantiate: (di) => {
|
||||
const lensProxy = di.inject(lensProxyInjectable);
|
||||
const exitApp = di.inject(exitAppInjectable);
|
||||
const logger = di.inject(loggerInjectable);
|
||||
const requestAppVersionViaProxy = di.inject(requestAppVersionViaProxyInjectable);
|
||||
const isWindows = di.inject(isWindowsInjectable);
|
||||
const showErrorPopup = di.inject(showErrorPopupInjectable);
|
||||
const buildVersion = di.inject(buildVersionInjectable);
|
||||
const setupLensProxyStartableStoppable = di.inject(setupLensProxyStartableStoppableInjectable);
|
||||
|
||||
return {
|
||||
id: "setup-lens-proxy",
|
||||
run: async () => {
|
||||
try {
|
||||
logger.info("🔌 Starting LensProxy");
|
||||
await lensProxy.listen(); // lensProxy.port available
|
||||
} catch (error: any) {
|
||||
showErrorPopup("Lens Error", `Could not start proxy: ${error?.message || "unknown error"}`);
|
||||
|
||||
return exitApp();
|
||||
}
|
||||
|
||||
// test proxy connection
|
||||
try {
|
||||
logger.info("🔎 Testing LensProxy connection ...");
|
||||
const versionFromProxy = await requestAppVersionViaProxy();
|
||||
|
||||
if (buildVersion.get() !== versionFromProxy) {
|
||||
logger.error("Proxy server responded with invalid response");
|
||||
|
||||
return exitApp();
|
||||
}
|
||||
|
||||
logger.info("⚡ LensProxy connection OK");
|
||||
} catch (error) {
|
||||
logger.error(`🛑 LensProxy: failed connection test: ${error}`);
|
||||
|
||||
const hostsPath = isWindows
|
||||
? "C:\\windows\\system32\\drivers\\etc\\hosts"
|
||||
: "/etc/hosts";
|
||||
const message = [
|
||||
`Failed connection test: ${error}`,
|
||||
"Check to make sure that no other versions of Lens are running",
|
||||
`Check ${hostsPath} to make sure that it is clean and that the localhost loopback is at the top and set to 127.0.0.1`,
|
||||
"If you have HTTP_PROXY or http_proxy set in your environment, make sure that the localhost and the ipv4 loopback address 127.0.0.1 are added to the NO_PROXY environment variable.",
|
||||
];
|
||||
|
||||
showErrorPopup("Lens Proxy Error", message.join("\n\n"));
|
||||
|
||||
return exitApp();
|
||||
}
|
||||
run: () => {
|
||||
setupLensProxyStartableStoppable.start();
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* 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 loggerInjectable from "../../../../common/logger.injectable";
|
||||
import { getStartableStoppable } from "../../../../common/utils/get-startable-stoppable";
|
||||
import isWindowsInjectable from "../../../../common/vars/is-windows.injectable";
|
||||
import exitAppInjectable from "../../../electron-app/features/exit-app.injectable";
|
||||
import showErrorPopupInjectable from "../../../electron-app/features/show-error-popup.injectable";
|
||||
import lensProxyInjectable from "../../../lens-proxy/lens-proxy.injectable";
|
||||
import buildVersionInjectable from "../../../vars/build-version/build-version.injectable";
|
||||
import requestAppVersionViaProxyInjectable from "./request-app-version.injectable";
|
||||
|
||||
const setupLensProxyStartableStoppableInjectable = getInjectable({
|
||||
id: "setup-lens-proxy-startable-stoppable",
|
||||
instantiate: (di) => {
|
||||
const lensProxy = di.inject(lensProxyInjectable);
|
||||
const exitApp = di.inject(exitAppInjectable);
|
||||
const logger = di.inject(loggerInjectable);
|
||||
const requestAppVersionViaProxy = di.inject(requestAppVersionViaProxyInjectable);
|
||||
const isWindows = di.inject(isWindowsInjectable);
|
||||
const showErrorPopup = di.inject(showErrorPopupInjectable);
|
||||
const buildVersion = di.inject(buildVersionInjectable);
|
||||
|
||||
return getStartableStoppable("setup-lens-proxy", () => {
|
||||
const controller = new AbortController();
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
logger.info("🔌 Starting LensProxy");
|
||||
await lensProxy.listen({ signal: controller.signal }); // lensProxy.port available
|
||||
} catch (error: any) {
|
||||
showErrorPopup("Lens Error", `Could not start proxy: ${error?.message || "unknown error"}`);
|
||||
|
||||
return exitApp();
|
||||
}
|
||||
|
||||
// test proxy connection
|
||||
try {
|
||||
logger.info("🔎 Testing LensProxy connection ...");
|
||||
const versionFromProxy = await requestAppVersionViaProxy({ signal: controller.signal });
|
||||
|
||||
if (buildVersion.get() !== versionFromProxy) {
|
||||
logger.error("Proxy server responded with invalid response");
|
||||
|
||||
return exitApp();
|
||||
}
|
||||
|
||||
logger.info("⚡ LensProxy connection OK");
|
||||
} catch (error) {
|
||||
logger.error(`🛑 LensProxy: failed connection test: ${error}`);
|
||||
|
||||
const hostsPath = isWindows
|
||||
? "C:\\windows\\system32\\drivers\\etc\\hosts"
|
||||
: "/etc/hosts";
|
||||
const message = [
|
||||
`Failed connection test: ${error}`,
|
||||
"Check to make sure that no other versions of Lens are running",
|
||||
`Check ${hostsPath} to make sure that it is clean and that the localhost loopback is at the top and set to 127.0.0.1`,
|
||||
"If you have HTTP_PROXY or http_proxy set in your environment, make sure that the localhost and the ipv4 loopback address 127.0.0.1 are added to the NO_PROXY environment variable.",
|
||||
];
|
||||
|
||||
showErrorPopup("Lens Proxy Error", message.join("\n\n"));
|
||||
|
||||
return exitApp();
|
||||
}
|
||||
})();
|
||||
|
||||
return () => controller.abort();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default setupLensProxyStartableStoppableInjectable;
|
||||
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* 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 { beforeQuitOfBackEndInjectionToken } from "../../runnable-tokens/before-quit-of-back-end-injection-token";
|
||||
import setupLensProxyStartableStoppableInjectable from "./startable-stoppable.injectable";
|
||||
|
||||
const stopSettingUpLensProxyInjectable = getInjectable({
|
||||
id: "stop-setting-up-lens-proxy",
|
||||
instantiate: (di) => {
|
||||
const setupLensProxyStartableStoppable = di.inject(setupLensProxyStartableStoppableInjectable);
|
||||
|
||||
return {
|
||||
id: "stop-setting-up-lens-proxy",
|
||||
run: () => {
|
||||
setupLensProxyStartableStoppable.stop();
|
||||
},
|
||||
};
|
||||
},
|
||||
injectionToken: beforeQuitOfBackEndInjectionToken,
|
||||
});
|
||||
|
||||
export default stopSettingUpLensProxyInjectable;
|
||||
Loading…
Reference in New Issue
Block a user