diff --git a/src/main/router.ts b/src/main/router.ts index 50175aa9de..16fed03734 100644 --- a/src/main/router.ts +++ b/src/main/router.ts @@ -180,7 +180,9 @@ export class Router { this.router.add({ method: "get", path: `${apiPrefix}/metrics/providers` }, MetricsRoute.routeMetricsProviders); // Port-forward API - this.router.add({ method: "post", path: `${apiPrefix}/pods/{namespace}/{resourceType}/{resourceName}/port-forward/{port}` }, PortForwardRoute.routePortForward); + this.router.add({ method: "post", path: `${apiPrefix}/pods/{namespace}/{resourceType}/{resourceName}/port-forward/{port}/{forwardPort}` }, PortForwardRoute.routePortForward); + this.router.add({ method: "get", path: `${apiPrefix}/pods/{namespace}/{resourceType}/{resourceName}/port-forward/{port}/{forwardPort}` }, PortForwardRoute.routeCurrentPortForward); + this.router.add({ method: "delete", path: `${apiPrefix}/pods/{namespace}/{resourceType}/{resourceName}/port-forward/{port}/{forwardPort}` }, PortForwardRoute.routeCurrentPortForwardStop); // Helm API this.router.add({ method: "get", path: `${apiPrefix}/v2/charts` }, HelmApiRoute.listCharts); diff --git a/src/main/routes/port-forward-route.ts b/src/main/routes/port-forward-route.ts index cfd88e771f..f4253a3571 100644 --- a/src/main/routes/port-forward-route.ts +++ b/src/main/routes/port-forward-route.ts @@ -34,6 +34,7 @@ interface PortForwardArgs { namespace: string; name: string; port: string; + forwardPort: string; } const internalPortRegex = /^forwarding from (?
.+) ->/i; @@ -58,6 +59,7 @@ class PortForward { public name: string; public port: string; public internalPort?: number; + public forwardPort: string; constructor(public kubeConfig: string, args: PortForwardArgs) { this.clusterId = args.clusterId; @@ -65,6 +67,7 @@ class PortForward { this.namespace = args.namespace; this.name = args.name; this.port = args.port; + this.forwardPort = args.forwardPort; } public async start() { @@ -74,7 +77,7 @@ class PortForward { "port-forward", "-n", this.namespace, `${this.kind}/${this.name}`, - `:${this.port}` + `${this.forwardPort??""}:${this.port}` ]; this.process = spawn(kubectlBin, args, { @@ -104,6 +107,10 @@ class PortForward { } } + public async stop() { + this.process.kill(); + } + public open() { shell.openExternal(`http://localhost:${this.internalPort}`) .catch(error => logger.error(`[PORT-FORWARD]: failed to open external shell: ${error}`, { @@ -118,33 +125,73 @@ class PortForward { export class PortForwardRoute { static async routePortForward(request: LensApiRequest) { + const { params, response, cluster } = request; + const { namespace, port, forwardPort, resourceType, resourceName } = params; + + try { + let portForward = PortForward.getPortforward({ + clusterId: cluster.id, kind: resourceType, name: resourceName, + namespace, port, forwardPort, + }); + + if (!portForward) { + logger.info(`Creating a new port-forward ${namespace}/${resourceType}/${resourceName}:${port}`); + portForward = new PortForward(await cluster.getProxyKubeconfigPath(), { + clusterId: cluster.id, + kind: resourceType, + namespace, + name: resourceName, + port, + forwardPort + }); + + const started = await portForward.start(); + + if (!started) { + return respondJson(response, { + message: `Failed to forward port ${port} to ${forwardPort}` + }, 400); + } + } + + portForward.open(); + respondJson(response, {port: portForward.forwardPort}); + } catch (e) { + return respondJson(response, { + message: `Failed to forward port ${port} to ${forwardPort}` + }, 400); + } + } + + static async routeCurrentPortForward(request: LensApiRequest) { const { params, response, cluster} = request; - const { namespace, port, resourceType, resourceName } = params; - let portForward = PortForward.getPortforward({ + const { namespace, port, forwardPort, resourceType, resourceName } = params; + + const portForward = PortForward.getPortforward({ clusterId: cluster.id, kind: resourceType, name: resourceName, - namespace, port + namespace, port, forwardPort }); - if (!portForward) { - logger.info(`Creating a new port-forward ${namespace}/${resourceType}/${resourceName}:${port}`); - portForward = new PortForward(await cluster.getProxyKubeconfigPath(), { - clusterId: cluster.id, - kind: resourceType, - namespace, - name: resourceName, - port, - }); - const started = await portForward.start(); + let forwardedPort = -1; - if (!started) { - return respondJson(response, { - message: "Failed to open port-forward" - }, 400); - } + if (portForward) { + forwardedPort = Number(portForward.forwardPort); } - portForward.open(); + respondJson(response, {port: forwardedPort}); + } - respondJson(response, {}); + static async routeCurrentPortForwardStop(request: LensApiRequest) { + const { params, response, cluster} = request; + const { namespace, port, forwardPort, resourceType, resourceName } = params; + + const portForward = PortForward.getPortforward({ + clusterId: cluster.id, kind: resourceType, name: resourceName, + namespace, port, forwardPort, + }); + + portForward.stop().then( () => { + respondJson(response, {status: true}); + }).catch(error => logger.error(error)); } } diff --git a/src/renderer/components/+network-services/service-port-component.scss b/src/renderer/components/+network-services/service-port-component.scss index 89369f560f..78153122e8 100644 --- a/src/renderer/components/+network-services/service-port-component.scss +++ b/src/renderer/components/+network-services/service-port-component.scss @@ -35,9 +35,10 @@ text-decoration: underline; } - .Spinner { - --spinner-size: #{$unit * 2}; - margin-left: $margin; - position: absolute; + .portInput { + display: inline-block !important; + width: 65px; + margin-left: 10px; + margin-right: 10px; } } diff --git a/src/renderer/components/+network-services/service-port-component.tsx b/src/renderer/components/+network-services/service-port-component.tsx index a0c4c32f49..864cec3d8b 100644 --- a/src/renderer/components/+network-services/service-port-component.tsx +++ b/src/renderer/components/+network-services/service-port-component.tsx @@ -28,20 +28,44 @@ import { apiBase } from "../../api"; import { observable, makeObservable } from "mobx"; import { cssNames } from "../../utils"; import { Notifications } from "../notifications"; -import { Spinner } from "../spinner"; +import { Input } from "../input"; +import { Button } from "../button"; interface Props { service: Service; port: ServicePort; } +interface PortForwardResult { + port: number; +} + @observer export class ServicePortComponent extends React.Component