From 353b79ab7646c11dd2f9844d3a06cbe7d6bf7095 Mon Sep 17 00:00:00 2001 From: Jim Ehrismann <40840436+jim-docker@users.noreply.github.com> Date: Mon, 25 Oct 2021 11:25:37 -0400 Subject: [PATCH] Support port forwarding https protocol (#4119) * added support for port-forwarding https Signed-off-by: Jim Ehrismann * added protocol to the port-forward list and details page. Added the port-forward address to the details page title Signed-off-by: Jim Ehrismann * predict the protocol based on the port name, some cleanup Signed-off-by: Jim Ehrismann * address review comments Signed-off-by: Jim Ehrismann * code reorg Signed-off-by: Jim Ehrismann * fix lint Signed-off-by: Jim Ehrismann --- src/main/routes/port-forward-route.ts | 25 +++++---- .../port-forward-details.tsx | 7 ++- .../port-forward-menu.tsx | 2 +- .../+network-port-forwards/port-forwards.tsx | 4 ++ .../service-port-component.tsx | 4 +- .../+workloads-pods/pod-container-port.tsx | 4 +- src/renderer/port-forward/index.ts | 1 + .../port-forward/port-forward-dialog.tsx | 13 ++++- .../port-forward/port-forward-item.ts | 7 +++ .../port-forward/port-forward-utils.ts | 53 +++++++++++++++++++ .../port-forward/port-forward.store.ts | 39 ++++++-------- 11 files changed, 120 insertions(+), 39 deletions(-) create mode 100644 src/renderer/port-forward/port-forward-utils.ts diff --git a/src/main/routes/port-forward-route.ts b/src/main/routes/port-forward-route.ts index 763ea16b43..56934628dd 100644 --- a/src/main/routes/port-forward-route.ts +++ b/src/main/routes/port-forward-route.ts @@ -34,6 +34,7 @@ interface PortForwardArgs { name: string; port: number; forwardPort: number; + protocol?: string; } const internalPortRegex = /^forwarding from (?
.+) ->/i; @@ -47,7 +48,8 @@ class PortForward { pf.kind == forward.kind && pf.name == forward.name && pf.namespace == forward.namespace && - pf.port == forward.port + pf.port == forward.port && + (!forward.protocol || pf.protocol == forward.protocol) )); } @@ -58,6 +60,7 @@ class PortForward { public name: string; public port: number; public forwardPort: number; + public protocol: string; constructor(public kubeConfig: string, args: PortForwardArgs) { this.clusterId = args.clusterId; @@ -66,6 +69,7 @@ class PortForward { this.name = args.name; this.port = args.port; this.forwardPort = args.forwardPort; + this.protocol = args.protocol ?? "http"; } public async start() { @@ -119,21 +123,21 @@ export class PortForwardRoute { const { namespace, resourceType, resourceName } = params; const port = Number(query.get("port")); const forwardPort = Number(query.get("forwardPort")); + const protocol = query.get("protocol"); try { let portForward = PortForward.getPortforward({ clusterId: cluster.id, kind: resourceType, name: resourceName, - namespace, port, forwardPort, + namespace, port, forwardPort, protocol, }); - let thePort = 0; - - if (forwardPort > 0 && forwardPort < 65536) { - thePort = forwardPort; - } - if (!portForward) { logger.info(`Creating a new port-forward ${namespace}/${resourceType}/${resourceName}:${port}`); + + const thePort = 0 < forwardPort && forwardPort < 65536 + ? forwardPort + : 0; + portForward = new PortForward(await cluster.getProxyKubeconfigPath(), { clusterId: cluster.id, kind: resourceType, @@ -141,6 +145,7 @@ export class PortForwardRoute { name: resourceName, port, forwardPort: thePort, + protocol, }); const started = await portForward.start(); @@ -169,10 +174,11 @@ export class PortForwardRoute { const { namespace, resourceType, resourceName } = params; const port = Number(query.get("port")); const forwardPort = Number(query.get("forwardPort")); + const protocol = query.get("protocol"); const portForward = PortForward.getPortforward({ clusterId: cluster.id, kind: resourceType, name: resourceName, - namespace, port, forwardPort + namespace, port, forwardPort, protocol, }); respondJson(response, { port: portForward?.forwardPort ?? null }); @@ -189,6 +195,7 @@ export class PortForwardRoute { name: f.name, port: f.port, forwardPort: f.forwardPort, + protocol: f.protocol, }) ); diff --git a/src/renderer/components/+network-port-forwards/port-forward-details.tsx b/src/renderer/components/+network-port-forwards/port-forward-details.tsx index 4fa4b395f7..73f8f2fe1f 100644 --- a/src/renderer/components/+network-port-forwards/port-forward-details.tsx +++ b/src/renderer/components/+network-port-forwards/port-forward-details.tsx @@ -23,7 +23,7 @@ import "./port-forward-details.scss"; import React from "react"; import { Link } from "react-router-dom"; -import type { PortForwardItem } from "../../port-forward"; +import { portForwardAddress, PortForwardItem } from "../../port-forward"; import { Drawer, DrawerItem } from "../drawer"; import { cssNames } from "../../utils"; import { podsApi, serviceApi } from "../../../common/k8s-api/endpoints"; @@ -80,6 +80,9 @@ export class PortForwardDetails extends React.Component { {portForward.getForwardPort()} + + {portForward.getProtocol()} + {portForward.getStatus()} @@ -96,7 +99,7 @@ export class PortForwardDetails extends React.Component { className="PortForwardDetails" usePortal={true} open={!!portForward} - title="Port Forward" + title={`Port Forward: ${portForwardAddress(portForward)}`} onClose={hideDetails} toolbar={toolbar} > diff --git a/src/renderer/components/+network-port-forwards/port-forward-menu.tsx b/src/renderer/components/+network-port-forwards/port-forward-menu.tsx index 2f6f8ea569..5b49c3f85c 100644 --- a/src/renderer/components/+network-port-forwards/port-forward-menu.tsx +++ b/src/renderer/components/+network-port-forwards/port-forward-menu.tsx @@ -57,7 +57,7 @@ export class PortForwardMenu extends React.Component { Open PortForwardDialog.open(portForward)}> - + Edit diff --git a/src/renderer/components/+network-port-forwards/port-forwards.tsx b/src/renderer/components/+network-port-forwards/port-forwards.tsx index 9fdeec4f6a..17b1c40c20 100644 --- a/src/renderer/components/+network-port-forwards/port-forwards.tsx +++ b/src/renderer/components/+network-port-forwards/port-forwards.tsx @@ -37,6 +37,7 @@ enum columnId { kind = "kind", port = "port", forwardPort = "forwardPort", + protocol = "protocol", status = "status", } @@ -102,6 +103,7 @@ export class PortForwards extends React.Component { [columnId.kind]: item => item.getKind(), [columnId.port]: item => item.getPort(), [columnId.forwardPort]: item => item.getForwardPort(), + [columnId.protocol]: item => item.getProtocol(), [columnId.status]: item => item.getStatus(), }} searchFilters={[ @@ -114,6 +116,7 @@ export class PortForwards extends React.Component { { title: "Kind", className: "kind", sortBy: columnId.kind, id: columnId.kind }, { title: "Pod Port", className: "port", sortBy: columnId.port, id: columnId.port }, { title: "Local Port", className: "forwardPort", sortBy: columnId.forwardPort, id: columnId.forwardPort }, + { title: "Protocol", className: "protocol", sortBy: columnId.protocol, id: columnId.protocol }, { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, ]} renderTableContents={item => [ @@ -122,6 +125,7 @@ export class PortForwards extends React.Component { item.getKind(), item.getPort(), item.getForwardPort(), + item.getProtocol(), { title: item.getStatus(), className: item.getStatus().toLowerCase() }, ]} renderItemMenu={pf => ( diff --git a/src/renderer/components/+network-services/service-port-component.tsx b/src/renderer/components/+network-services/service-port-component.tsx index ce9f731b21..6a95846e04 100644 --- a/src/renderer/components/+network-services/service-port-component.tsx +++ b/src/renderer/components/+network-services/service-port-component.tsx @@ -28,7 +28,7 @@ import { observable, makeObservable, reaction } from "mobx"; import { cssNames } from "../../utils"; import { Notifications } from "../notifications"; import { Button } from "../button"; -import { addPortForward, getPortForward, openPortForward, PortForwardDialog, portForwardStore, removePortForward } from "../../port-forward"; +import { addPortForward, getPortForward, openPortForward, PortForwardDialog, portForwardStore, predictProtocol, removePortForward } from "../../port-forward"; import type { ForwardedPort } from "../../port-forward"; import { Spinner } from "../spinner"; @@ -87,6 +87,7 @@ export class ServicePortComponent extends React.Component { namespace: service.getNs(), port: port.port, forwardPort: this.forwardPort, + protocol: predictProtocol(port.name), }; this.waiting = true; @@ -143,6 +144,7 @@ export class ServicePortComponent extends React.Component { namespace: service.getNs(), port: port.port, forwardPort: this.forwardPort, + protocol: predictProtocol(port.name), }; PortForwardDialog.open(portForward, { openInBrowser: true }); diff --git a/src/renderer/components/+workloads-pods/pod-container-port.tsx b/src/renderer/components/+workloads-pods/pod-container-port.tsx index 41bb4c9736..19f0c5446b 100644 --- a/src/renderer/components/+workloads-pods/pod-container-port.tsx +++ b/src/renderer/components/+workloads-pods/pod-container-port.tsx @@ -28,7 +28,7 @@ import { observable, makeObservable, reaction } from "mobx"; import { cssNames } from "../../utils"; import { Notifications } from "../notifications"; import { Button } from "../button"; -import { addPortForward, getPortForward, openPortForward, PortForwardDialog, portForwardStore, removePortForward } from "../../port-forward"; +import { addPortForward, getPortForward, openPortForward, PortForwardDialog, portForwardStore, predictProtocol, removePortForward } from "../../port-forward"; import type { ForwardedPort } from "../../port-forward"; import { Spinner } from "../spinner"; @@ -91,6 +91,7 @@ export class PodContainerPort extends React.Component { namespace: pod.getNs(), port: port.containerPort, forwardPort: this.forwardPort, + protocol: predictProtocol(port.name), }; this.waiting = true; @@ -149,6 +150,7 @@ export class PodContainerPort extends React.Component { namespace: pod.getNs(), port: port.containerPort, forwardPort: this.forwardPort, + protocol: predictProtocol(port.name), }; PortForwardDialog.open(portForward, { openInBrowser: true }); diff --git a/src/renderer/port-forward/index.ts b/src/renderer/port-forward/index.ts index 5be9fb5aed..83a548f933 100644 --- a/src/renderer/port-forward/index.ts +++ b/src/renderer/port-forward/index.ts @@ -22,3 +22,4 @@ export * from "./port-forward.store"; export * from "./port-forward-item"; export * from "./port-forward-dialog"; +export * from "./port-forward-utils"; diff --git a/src/renderer/port-forward/port-forward-dialog.tsx b/src/renderer/port-forward/port-forward-dialog.tsx index 2ce330065e..3c1306fe33 100644 --- a/src/renderer/port-forward/port-forward-dialog.tsx +++ b/src/renderer/port-forward/port-forward-dialog.tsx @@ -44,7 +44,8 @@ interface PortForwardDialogOpenOptions { const dialogState = observable.object({ isOpen: false, data: null as ForwardedPort, - openInBrowser: false + useHttps: false, + openInBrowser: false, }); @observer @@ -60,6 +61,7 @@ export class PortForwardDialog extends Component { static open(portForward: ForwardedPort, options: PortForwardDialogOpenOptions = { openInBrowser: false }) { dialogState.isOpen = true; dialogState.data = portForward; + dialogState.useHttps = portForward.protocol === "https"; dialogState.openInBrowser = options.openInBrowser; } @@ -96,6 +98,8 @@ export class PortForwardDialog extends Component { try { let port: number; + portForward.protocol = dialogState.useHttps ? "https" : "http"; + if (currentPort) { port = await modifyPortForward(portForward, desiredPort); } else { @@ -131,6 +135,13 @@ export class PortForwardDialog extends Component { onChange={this.changePort} /> + dialogState.useHttps = value} + /> { + logger.error(`failed to open in browser: ${error}`, { + clusterId: portForward.clusterId, + port: portForward.port, + kind: portForward.kind, + namespace: portForward.namespace, + name: portForward.name, + }); + Notifications.error(`Failed to open ${browseTo} in browser`); + } + ); + +} + +export function predictProtocol(name: string) { + return name === "https" ? "https" : "http"; +} + diff --git a/src/renderer/port-forward/port-forward.store.ts b/src/renderer/port-forward/port-forward.store.ts index 1f23765f81..4fc9c81540 100644 --- a/src/renderer/port-forward/port-forward.store.ts +++ b/src/renderer/port-forward/port-forward.store.ts @@ -22,11 +22,10 @@ import { makeObservable, observable, reaction } from "mobx"; import { ItemStore } from "../../common/item.store"; -import { autoBind, createStorage, disposer, getHostedClusterId, openExternal } from "../utils"; +import { autoBind, createStorage, disposer, getHostedClusterId } from "../utils"; import { ForwardedPort, PortForwardItem } from "./port-forward-item"; import { apiBase } from "../api"; import { waitUntilFree } from "tcp-port-used"; -import { Notifications } from "../components/notifications"; import logger from "../../common/logger"; export class PortForwardStore extends ItemStore { @@ -88,7 +87,7 @@ export class PortForwardStore extends ItemStore { if (index === -1) { return null; } - + return this.getItems()[index]; } } @@ -105,8 +104,10 @@ export async function addPortForward(portForward: ForwardedPort): Promise(`/pods/port-forward/${portForward.namespace}/${portForward.kind}/${portForward.name}?port=${portForward.port}&forwardPort=${portForward.forwardPort}`); - + const protocol = portForward.protocol ?? "http"; + + response = await apiBase.post(`/pods/port-forward/${portForward.namespace}/${portForward.kind}/${portForward.name}?port=${portForward.port}&forwardPort=${portForward.forwardPort}&protocol=${protocol}`); + if (response?.port && response.port != +portForward.forwardPort) { logger.warn(`specified ${portForward.forwardPort} got ${response.port}`); } @@ -119,11 +120,19 @@ export async function addPortForward(portForward: ForwardedPort): Promise { let response: PortForwardResult; try { - response = await apiBase.get(`/pods/port-forward/${portForward.namespace}/${portForward.kind}/${portForward.name}?port=${portForward.port}&forwardPort=${portForward.forwardPort}`); + response = await apiBase.get(`/pods/port-forward/${portForward.namespace}/${portForward.kind}/${portForward.name}?port=${portForward.port}&forwardPort=${portForward.forwardPort}${getProtocolQuery(portForward.protocol)}`); } catch (error) { logger.warn("Error getting port-forward:", error, portForward); throw(error); @@ -168,22 +177,4 @@ export async function getPortForwards(): Promise { } } -export function openPortForward(portForward: ForwardedPort) { - const browseTo = `http://localhost:${portForward.forwardPort}`; - - openExternal(browseTo) - .catch(error => { - logger.error(`failed to open in browser: ${error}`, { - clusterId: portForward.clusterId, - port: portForward.port, - kind: portForward.kind, - namespace: portForward.namespace, - name: portForward.name, - }); - Notifications.error(`Failed to open ${browseTo} in browser`); - } - ); - -} - export const portForwardStore = new PortForwardStore();