From 693017d2ec87a971d35a9dc1e628e507737f3e4d Mon Sep 17 00:00:00 2001 From: Jim Ehrismann <40840436+jim-docker@users.noreply.github.com> Date: Fri, 7 Aug 2020 11:30:37 -0400 Subject: [PATCH] adding port-forward for containers in pods (#528) * adding port-forward for containers in pods address review comments use more idiomatic approach for async code move some files in advance of merge conflict with Lens restructure work * Separate the port forward links in the UI (so they don't all spin when one link is clicked) * minor fixes * addressed review comments (replaced

with

, moved key attribute to proper element) * fix lint issue * removed extraneous
from pod container port details Signed-off-by: Jim Ehrismann --- src/main/router.ts | 2 +- src/main/routes/port-forward.ts | 21 ++++---- .../+network-services/service-details.tsx | 10 +++- .../service-port-component.scss | 22 ++++++++ .../service-port-component.tsx | 48 +++++++++++++++++ .../+network-services/service-ports.scss | 24 --------- .../+network-services/service-ports.tsx | 54 ------------------- .../+workloads-pods/pod-container-port.scss | 23 ++++++++ .../+workloads-pods/pod-container-port.tsx | 54 +++++++++++++++++++ .../+workloads-pods/pod-details-container.tsx | 12 ++--- 10 files changed, 172 insertions(+), 98 deletions(-) create mode 100644 src/renderer/components/+network-services/service-port-component.scss create mode 100644 src/renderer/components/+network-services/service-port-component.tsx delete mode 100644 src/renderer/components/+network-services/service-ports.scss delete mode 100644 src/renderer/components/+network-services/service-ports.tsx create mode 100644 src/renderer/components/+workloads-pods/pod-container-port.scss create mode 100644 src/renderer/components/+workloads-pods/pod-container-port.tsx diff --git a/src/main/router.ts b/src/main/router.ts index 9ee3a47c73..3381fa139d 100644 --- a/src/main/router.ts +++ b/src/main/router.ts @@ -123,7 +123,7 @@ export class Router { this.router.add({ method: "post", path: `${apiBase}/metrics` }, metricsRoute.routeMetrics.bind(metricsRoute)) // Port-forward API - this.router.add({ method: "post", path: `${apiBase}/services/{namespace}/{service}/port-forward/{port}` }, portForwardRoute.routeServicePortForward.bind(portForwardRoute)) + this.router.add({ method: "post", path: `${apiBase}/pods/{namespace}/{resourceType}/{resourceName}/port-forward/{port}` }, portForwardRoute.routePortForward.bind(portForwardRoute)) // Helm API this.router.add({ method: "get", path: `${apiHelm}/v2/charts` }, helmApi.listCharts.bind(helmApi)) diff --git a/src/main/routes/port-forward.ts b/src/main/routes/port-forward.ts index 27b1158700..ca222596a1 100644 --- a/src/main/routes/port-forward.ts +++ b/src/main/routes/port-forward.ts @@ -14,7 +14,7 @@ class PortForward { return PortForward.portForwards.find((pf) => { return ( pf.clusterId == forward.clusterId && - pf.kind == "service" && + pf.kind == forward.kind && pf.name == forward.name && pf.namespace == forward.namespace && pf.port == forward.port @@ -42,7 +42,7 @@ class PortForward { "--kubeconfig", this.kubeConfig, "port-forward", "-n", this.namespace, - `service/${this.name}`, + `${this.kind}/${this.name}`, `${this.localPort}:${this.port}` ] @@ -72,21 +72,22 @@ class PortForward { class PortForwardRoute extends LensApi { - public async routeServicePortForward(request: LensApiRequest) { + public async routePortForward(request: LensApiRequest) { const { params, response, cluster} = request + const { namespace, port, resourceType, resourceName } = params let portForward = PortForward.getPortforward({ - clusterId: cluster.id, kind: "service", name: params.service, - namespace: params.namespace, port: params.port + clusterId: cluster.id, kind: resourceType, name: resourceName, + namespace: namespace, port: port }) if (!portForward) { - logger.info(`Creating a new port-forward ${params.namespace}/${params.service}:${params.port}`) + logger.info(`Creating a new port-forward ${namespace}/${resourceType}/${resourceName}:${port}`) portForward = new PortForward({ clusterId: cluster.id, - kind: "service", - namespace: params.namespace, - name: params.service, - port: params.port, + kind: resourceType, + namespace: namespace, + name: resourceName, + port: port, kubeConfig: cluster.proxyKubeconfigPath() }) const started = await portForward.start() diff --git a/src/renderer/components/+network-services/service-details.tsx b/src/renderer/components/+network-services/service-details.tsx index 0c158c9e6b..df37d6238e 100644 --- a/src/renderer/components/+network-services/service-details.tsx +++ b/src/renderer/components/+network-services/service-details.tsx @@ -11,7 +11,7 @@ import { Service, serviceApi, endpointApi } from "../../api/endpoints"; import { _i18n } from "../../i18n"; import { apiManager } from "../../api/api-manager"; import { KubeObjectMeta } from "../kube-object/kube-object-meta"; -import { ServicePorts } from "./service-ports"; +import { ServicePortComponent } from "./service-port-component"; import { endpointStore } from "../+network-endpoints/endpoints.store"; import { ServiceDetailsEndpoint } from "./service-details-endpoint"; @@ -61,7 +61,13 @@ export class ServiceDetails extends React.Component { )} Ports}> - +
+ { + service.getPorts().map((port) => ( + + )) + } +
{spec.type === "LoadBalancer" && spec.loadBalancerIP && ( diff --git a/src/renderer/components/+network-services/service-port-component.scss b/src/renderer/components/+network-services/service-port-component.scss new file mode 100644 index 0000000000..0e9945631d --- /dev/null +++ b/src/renderer/components/+network-services/service-port-component.scss @@ -0,0 +1,22 @@ +.ServicePortComponent { + &.waiting { + opacity: 0.5; + pointer-events: none; + } + + &:not(:last-child) { + margin-bottom: $margin; + } + + span { + cursor: pointer; + color: $primary; + text-decoration: underline; + } + + .Spinner { + --spinner-size: #{$unit * 2}; + margin-left: $margin; + position: absolute; + } +} diff --git a/src/renderer/components/+network-services/service-port-component.tsx b/src/renderer/components/+network-services/service-port-component.tsx new file mode 100644 index 0000000000..252bf8eb16 --- /dev/null +++ b/src/renderer/components/+network-services/service-port-component.tsx @@ -0,0 +1,48 @@ +import "./service-port-component.scss" + +import React from "react"; +import { observer } from "mobx-react"; +import { t } from "@lingui/macro"; +import { Service, ServicePort } from "../../api/endpoints"; +import { _i18n } from "../../i18n"; +import { apiBase } from "../../api" +import { observable } from "mobx"; +import { cssNames } from "../../utils"; +import { Notifications } from "../notifications"; +import { Spinner } from "../spinner" + +interface Props { + service: Service; + port: ServicePort; +} + +@observer +export class ServicePortComponent extends React.Component { + @observable waiting = false; + + async portForward() { + const { service, port } = this.props; + this.waiting = true; + try { + await apiBase.post(`/pods/${service.getNs()}/service/${service.getName()}/port-forward/${port.port}`, {}) + } catch(error) { + Notifications.error(error); + } finally { + this.waiting = false; + } + } + + render() { + const { port } = this.props; + return ( +
+ this.portForward() }> + {port.toString()} + {this.waiting && ( + + )} + +
+ ); + } +} diff --git a/src/renderer/components/+network-services/service-ports.scss b/src/renderer/components/+network-services/service-ports.scss deleted file mode 100644 index 5a683af86c..0000000000 --- a/src/renderer/components/+network-services/service-ports.scss +++ /dev/null @@ -1,24 +0,0 @@ -.ServicePorts { - &.waiting { - opacity: 0.5; - pointer-events: none; - } - - p { - &:not(:last-child) { - margin-bottom: $margin; - } - - span { - cursor: pointer; - color: $primary; - text-decoration: underline; - } - } - - .Spinner { - --spinner-size: #{$unit * 2}; - margin-left: $margin; - position: absolute; - } -} diff --git a/src/renderer/components/+network-services/service-ports.tsx b/src/renderer/components/+network-services/service-ports.tsx deleted file mode 100644 index 3335be6907..0000000000 --- a/src/renderer/components/+network-services/service-ports.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import "./service-ports.scss" - -import React from "react"; -import { observer } from "mobx-react"; -import { t } from "@lingui/macro"; -import { Service, ServicePort } from "../../api/endpoints"; -import { _i18n } from "../../i18n"; -import { apiBase } from "../../api" -import { observable } from "mobx"; -import { cssNames } from "../../utils"; -import { Notifications } from "../notifications"; -import { Spinner } from "../spinner" - -interface Props { - service: Service; -} - -@observer -export class ServicePorts extends React.Component { - @observable waiting = false; - - async portForward(port: ServicePort) { - const { service } = this.props; - this.waiting = true; - apiBase.post(`/services/${service.getNs()}/${service.getName()}/port-forward/${port.port}`, {}) - .catch(error => { - Notifications.error(error); - }) - .finally(() => { - this.waiting = false; - }); - } - - render() { - const { service } = this.props; - return ( -
- { - service.getPorts().map((port) => { - return( -

- this.portForward(port) }> - {port.toString()} - {this.waiting && ( - - )} - -

- ); - })} -
- ); - } -} diff --git a/src/renderer/components/+workloads-pods/pod-container-port.scss b/src/renderer/components/+workloads-pods/pod-container-port.scss new file mode 100644 index 0000000000..081f0b1090 --- /dev/null +++ b/src/renderer/components/+workloads-pods/pod-container-port.scss @@ -0,0 +1,23 @@ +.PodContainerPort { + &.waiting { + opacity: 0.5; + pointer-events: none; + } + + &:not(:last-child) { + margin-bottom: $margin; + } + + span { + cursor: pointer; + color: $primary; + text-decoration: underline; + position: relative; + } + + .Spinner { + --spinner-size: #{$unit * 2}; + margin-left: $margin; + position: absolute; + } +} \ No newline at end of file diff --git a/src/renderer/components/+workloads-pods/pod-container-port.tsx b/src/renderer/components/+workloads-pods/pod-container-port.tsx new file mode 100644 index 0000000000..9ebaed4fd7 --- /dev/null +++ b/src/renderer/components/+workloads-pods/pod-container-port.tsx @@ -0,0 +1,54 @@ +import "./pod-container-port.scss" + +import React from "react"; +import { observer } from "mobx-react"; +import { t } from "@lingui/macro"; +import { Pod, IPodContainer } from "../../api/endpoints"; +import { _i18n } from "../../i18n"; +import { apiBase } from "../../api" +import { observable } from "mobx"; +import { cssNames } from "../../utils"; +import { Notifications } from "../notifications"; +import { Spinner } from "../spinner" + +interface Props { + pod: Pod; + port: { + name?: string; + containerPort: number; + protocol: string; + } +} + +@observer +export class PodContainerPort extends React.Component { + @observable waiting = false; + + async portForward() { + const { pod, port } = this.props; + this.waiting = true; + try { + await apiBase.post(`/pods/${pod.getNs()}/pod/${pod.getName()}/port-forward/${port.containerPort}`, {}) + } catch(error) { + Notifications.error(error); + } finally { + this.waiting = false; + } + } + + render() { + const { port } = this.props; + const { name, containerPort, protocol } = port; + const text = (name ? name + ': ' : '')+`${containerPort}/${protocol}` + return ( +
+ this.portForward() }> + {text} + {this.waiting && ( + + )} + +
+ ) + } +} diff --git a/src/renderer/components/+workloads-pods/pod-details-container.tsx b/src/renderer/components/+workloads-pods/pod-details-container.tsx index a5cdcc1fbd..79180b1130 100644 --- a/src/renderer/components/+workloads-pods/pod-details-container.tsx +++ b/src/renderer/components/+workloads-pods/pod-details-container.tsx @@ -8,6 +8,7 @@ import { cssNames } from "../../utils"; import { StatusBrick } from "../status-brick"; import { Badge } from "../badge"; import { ContainerEnvironment } from "./pod-container-env"; +import { PodContainerPort } from "./pod-container-port"; import { ResourceMetrics } from "../resource-metrics"; import { IMetrics } from "../../api/endpoints/metrics.api"; import { ContainerCharts } from "./container-charts"; @@ -64,13 +65,10 @@ export class PodDetailsContainer extends React.Component { {ports && ports.length > 0 && Ports}> { - ports.map(port => { - const { name, containerPort, protocol } = port; - const key = `${container.name}-port-${containerPort}-${protocol}` - return ( -
- {name ? name + ': ' : ''}{containerPort}/{protocol} -
+ ports.map((port) => { + const key = `${container.name}-port-${port.containerPort}-${port.protocol}` + return( + ) }) }