Feature/port forward dashboard (#3922)

* Added ability to add custom port on pod and service port forwarding (#3295)

* Added ability to add custom port on pod and service port forwarding

Signed-off-by: rdeepc <12953177+rdeepc@users.noreply.github.com>

* Added ability to add custom port on pod and service port forwarding

- pod-container-port fixed init async and active port checking
- service-port-component fixed init async and active port checking
- port-forward-route promise and response fix

Signed-off-by: rdeepc <12953177+rdeepc@users.noreply.github.com>

* Added ability to add custom port on pod and service port forwarding

Signed-off-by: rdeepc <12953177+rdeepc@users.noreply.github.com>

* - Added Custom Port Selection for port forwarding
- Implemented Random Port if custom port is not provided

Signed-off-by: rdeepc <12953177+rdeepc@users.noreply.github.com>

Co-authored-by: Saumya Shovan Roy <saumyashovanroy@gmail.com>

* cherry-pick of hackweek work plus merge conflicts/build errors

    added a route to get all port forwards

    Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>

    Added Forwarded Ports to cluster dashboard

    Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>

    working port-forward page (open, edit, remove)

    Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>

    added local storage to the port-forward store

    Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>

    automatically restore port-forward after pod is restarted

    Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>

    start port-forwards using random local port by default, rearranged pod and service port-forward UI

    Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>

Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>

* refactor

Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>

* more refactoring, don't always open port-forwards in browser, refined reused port-forward dialog

Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>

* removed unimplemented forwarded port details page,modified logging

Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>

* addressed some review comments

Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>

* made port and forwardPort query params and cleaned up port-forward routing paths

Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>

* address more review comments and change dashboard tab name to 'Port Forwarding'

Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>

* changed port and forwardPort fields to be Numbers

Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>

* removed extraneous reset() call, reorder field declarations

Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>

* port-forward now gets the bundled kubectl path without going through the 'ensureKubectl' hoops

Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>

* more cleanup/tweaking

Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>

* fix bug where port-forward info did not update on pod details page when different pod (in same deployment?) is clicked

Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>

Co-authored-by: Saumya Shovan Roy (Deep) <12953177+rdeepc@users.noreply.github.com>
Co-authored-by: Saumya Shovan Roy <saumyashovanroy@gmail.com>
This commit is contained in:
authored and GitHub committed 2021-10-07 15:51:26 -04:00
1 parent e10c13cdf4
commit 07c8177a97
22 files changed
+1137 -73

No files matched your search

@@ -22,12 +22,15 @@
import "./pod-container-port.scss";
import React from "react";
import { observer } from "mobx-react";
import { disposeOnUnmount, observer } from "mobx-react";
import type { Pod } from "../../../common/k8s-api/endpoints";
import { apiBase } from "../../api";
import { observable, makeObservable } from "mobx";
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 type { ForwardedPort } from "../../port-forward";
import logger from "../../../common/logger";
import { Spinner } from "../spinner";
interface Props {
@@ -42,20 +45,86 @@ interface Props {
@observer
export class PodContainerPort extends React.Component<Props> {
@observable waiting = false;
@observable forwardPort = 0;
@observable isPortForwarded = false;
constructor(props: Props) {
super(props);
makeObservable(this);
this.init();
}
componentDidMount() {
disposeOnUnmount(this, [
reaction(() => [ portForwardStore.portForwards, this.props.pod ], () => this.init()),
]);
}
init() {
this.checkExistingPortForwarding().catch(error => {
logger.error(error);
});
}
async checkExistingPortForwarding() {
const { pod, port } = this.props;
const portForward: ForwardedPort = {
kind: "pod",
name: pod.getName(),
namespace: pod.getNs(),
port: port.containerPort,
forwardPort: this.forwardPort,
};
const activePort = await getPortForward(portForward) ?? 0;
this.forwardPort = activePort;
this.isPortForwarded = activePort ? true : false;
}
async portForward() {
const { pod, port } = this.props;
const portForward: ForwardedPort = {
kind: "pod",
name: pod.getName(),
namespace: pod.getNs(),
port: port.containerPort,
forwardPort: this.forwardPort,
};
this.waiting = true;
this.isPortForwarded = false;
try {
this.forwardPort = await addPortForward(portForward);
if (this.forwardPort) {
portForward.forwardPort = this.forwardPort;
openPortForward(portForward);
this.isPortForwarded = true;
}
} catch (error) {
Notifications.error(error);
} finally {
this.waiting = false;
}
}
async stopPortForward() {
const { pod, port } = this.props;
const portForward: ForwardedPort = {
kind: "pod",
name: pod.getName(),
namespace: pod.getNs(),
port: port.containerPort,
forwardPort: this.forwardPort,
};
this.waiting = true;
try {
await apiBase.post(`/pods/${pod.getNs()}/pod/${pod.getName()}/port-forward/${port.containerPort}`, {});
} catch(error) {
await removePortForward(portForward);
this.isPortForwarded = false;
} catch (error) {
Notifications.error(error);
} finally {
this.waiting = false;
@@ -63,18 +132,35 @@ export class PodContainerPort extends React.Component<Props> {
}
render() {
const { port } = this.props;
const { pod, port } = this.props;
const { name, containerPort, protocol } = port;
const text = `${name ? `${name}: ` : ""}${containerPort}/${protocol}`;
const portForwardAction = async () => {
if (this.isPortForwarded) {
await this.stopPortForward();
} else {
const portForward: ForwardedPort = {
kind: "pod",
name: pod.getName(),
namespace: pod.getNs(),
port: port.containerPort,
forwardPort: this.forwardPort,
};
PortForwardDialog.open(portForward, { openInBrowser: true });
}
};
return (
<div className={cssNames("PodContainerPort", { waiting: this.waiting })}>
<span title="Open in a browser" onClick={() => this.portForward() }>
<span title="Open in a browser" onClick={() => this.portForward()}>
{text}
{this.waiting && (
<Spinner />
)}
</span>
<Button onClick={() => portForwardAction()}> {this.isPortForwarded ? "Stop" : "Forward..."} </Button>
{this.waiting && (
<Spinner />
)}
</div>
);
}