mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge remote-tracking branch 'origin/master' into eliminate-gst-from-app-paths
Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
80 files changed
+1822
-1271
No files matched your search
@@ -18,17 +18,20 @@
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import { noop } from "lodash/fp";
|
||||
import { action, computed, observable, makeObservable } from "mobx";
|
||||
import type { ForwardedPort } from "../port-forward-item";
|
||||
|
||||
interface PortForwardDialogOpenOptions {
|
||||
openInBrowser: boolean
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
export class PortForwardDialogModel {
|
||||
portForward: ForwardedPort = null;
|
||||
useHttps = false;
|
||||
openInBrowser = false;
|
||||
onClose = noop;
|
||||
|
||||
constructor() {
|
||||
makeObservable(this, {
|
||||
@@ -46,10 +49,11 @@ export class PortForwardDialogModel {
|
||||
return !!this.portForward;
|
||||
}
|
||||
|
||||
open = (portForward: ForwardedPort, options: PortForwardDialogOpenOptions = { openInBrowser: false }) => {
|
||||
open = (portForward: ForwardedPort, options: PortForwardDialogOpenOptions = { openInBrowser: false, onClose: noop }) => {
|
||||
this.portForward = portForward;
|
||||
this.useHttps = portForward.protocol === "https";
|
||||
this.openInBrowser = options.openInBrowser;
|
||||
this.onClose = options.onClose;
|
||||
};
|
||||
|
||||
close = () => {
|
||||
|
||||
@@ -27,25 +27,21 @@ import { observer } from "mobx-react";
|
||||
import { Dialog, DialogProps } from "../components/dialog";
|
||||
import { Wizard, WizardStep } from "../components/wizard";
|
||||
import { Input } from "../components/input";
|
||||
import { Notifications } from "../components/notifications";
|
||||
import { cssNames } from "../utils";
|
||||
import { getPortForwards } from "./port-forward-store/port-forward-store";
|
||||
import type { ForwardedPort } from "./port-forward-item";
|
||||
import type { PortForwardStore } from "./port-forward-store/port-forward-store";
|
||||
import { openPortForward } from "./port-forward-utils";
|
||||
import { aboutPortForwarding } from "./port-forward-notify";
|
||||
import { aboutPortForwarding, notifyErrorPortForwarding } from "./port-forward-notify";
|
||||
import { Checkbox } from "../components/checkbox";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import modifyPortForwardInjectable from "./port-forward-store/modify-port-forward/modify-port-forward.injectable";
|
||||
import type { PortForwardDialogModel } from "./port-forward-dialog-model/port-forward-dialog-model";
|
||||
import portForwardDialogModelInjectable from "./port-forward-dialog-model/port-forward-dialog-model.injectable";
|
||||
import addPortForwardInjectable from "./port-forward-store/add-port-forward/add-port-forward.injectable";
|
||||
import logger from "../../common/logger";
|
||||
import portForwardStoreInjectable from "./port-forward-store/port-forward-store.injectable";
|
||||
|
||||
interface Props extends Partial<DialogProps> {
|
||||
}
|
||||
interface Props extends Partial<DialogProps> {}
|
||||
|
||||
interface Dependencies {
|
||||
modifyPortForward: (item: ForwardedPort, desiredPort: number) => Promise<number>,
|
||||
addPortForward: (item: ForwardedPort) => Promise<number>,
|
||||
portForwardStore: PortForwardStore,
|
||||
model: PortForwardDialogModel
|
||||
}
|
||||
|
||||
@@ -59,50 +55,58 @@ class NonInjectedPortForwardDialog extends Component<Props & Dependencies> {
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
get portForwardStore() {
|
||||
return this.props.portForwardStore;
|
||||
}
|
||||
|
||||
onOpen = async () => {
|
||||
this.currentPort = +this.props.model.portForward.forwardPort;
|
||||
this.desiredPort = this.currentPort;
|
||||
};
|
||||
|
||||
onClose = () => {
|
||||
};
|
||||
|
||||
changePort = (value: string) => {
|
||||
this.desiredPort = Number(value);
|
||||
};
|
||||
|
||||
startPortForward = async () => {
|
||||
const portForward = this.props.model.portForward;
|
||||
let { portForward } = this.props.model;
|
||||
const { currentPort, desiredPort } = this;
|
||||
|
||||
try {
|
||||
// determine how many port-forwards are already active
|
||||
const { length } = await getPortForwards();
|
||||
|
||||
let port: number;
|
||||
// determine how many port-forwards already exist
|
||||
const { length } = this.portForwardStore.getPortForwards();
|
||||
|
||||
portForward.protocol = this.props.model.useHttps ? "https" : "http";
|
||||
|
||||
if (currentPort) {
|
||||
port = await this.props.modifyPortForward(portForward, desiredPort);
|
||||
const wasRunning = portForward.status === "Active";
|
||||
|
||||
portForward = await this.portForwardStore.modify(portForward, desiredPort);
|
||||
|
||||
if (wasRunning && portForward.status === "Disabled") {
|
||||
notifyErrorPortForwarding(`Error occurred starting port-forward, the local port ${portForward.forwardPort} may not be available or the ${portForward.kind} ${portForward.name} may not be reachable`);
|
||||
}
|
||||
} else {
|
||||
portForward.forwardPort = desiredPort;
|
||||
port = await this.props.addPortForward(portForward);
|
||||
portForward = await this.portForwardStore.add(portForward);
|
||||
|
||||
// if this is the first port-forward show the about notification
|
||||
if (!length) {
|
||||
aboutPortForwarding();
|
||||
if (portForward.status === "Disabled") {
|
||||
notifyErrorPortForwarding(`Error occurred starting port-forward, the local port ${portForward.forwardPort} may not be available or the ${portForward.kind} ${portForward.name} may not be reachable`);
|
||||
} else {
|
||||
// if this is the first port-forward show the about notification
|
||||
if (!length) {
|
||||
aboutPortForwarding();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.props.model.openInBrowser) {
|
||||
portForward.forwardPort = port;
|
||||
if (portForward.status === "Active" && this.props.model.openInBrowser) {
|
||||
openPortForward(portForward);
|
||||
}
|
||||
} catch (err) {
|
||||
Notifications.error(`Error occurred starting port-forward, the local port may not be available or the ${portForward.kind} ${portForward.name} may not be reachable`);
|
||||
} catch (error) {
|
||||
logger.error(`[PORT-FORWARD-DIALOG]: ${error}`, portForward);
|
||||
} finally {
|
||||
close();
|
||||
this.props.model.close();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -141,7 +145,7 @@ class NonInjectedPortForwardDialog extends Component<Props & Dependencies> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, modifyPortForward, model, ...dialogProps } = this.props;
|
||||
const { className, portForwardStore, model, ...dialogProps } = this.props;
|
||||
const resourceName = this.props.model.portForward?.name ?? "";
|
||||
const header = (
|
||||
<h5>
|
||||
@@ -155,14 +159,14 @@ class NonInjectedPortForwardDialog extends Component<Props & Dependencies> {
|
||||
isOpen={this.props.model.isOpen}
|
||||
className={cssNames("PortForwardDialog", className)}
|
||||
onOpen={this.onOpen}
|
||||
onClose={this.onClose}
|
||||
onClose={model.onClose}
|
||||
close={this.props.model.close}
|
||||
>
|
||||
<Wizard header={header} done={this.props.model.close}>
|
||||
<WizardStep
|
||||
contentClass="flex gaps column"
|
||||
next={this.startPortForward}
|
||||
nextLabel={this.currentPort === 0 ? "Start" : "Restart"}
|
||||
nextLabel={this.currentPort === 0 ? "Start" : "Modify"}
|
||||
>
|
||||
{this.renderContents()}
|
||||
</WizardStep>
|
||||
@@ -177,8 +181,7 @@ export const PortForwardDialog = withInjectables<Dependencies, Props>(
|
||||
|
||||
{
|
||||
getProps: (di, props) => ({
|
||||
modifyPortForward: di.inject(modifyPortForwardInjectable),
|
||||
addPortForward: di.inject(addPortForwardInjectable),
|
||||
portForwardStore: di.inject(portForwardStoreInjectable),
|
||||
model: di.inject(portForwardDialogModelInjectable),
|
||||
...props,
|
||||
}),
|
||||
|
||||
@@ -23,33 +23,34 @@
|
||||
import type { ItemObject } from "../../common/item.store";
|
||||
import { autoBind } from "../../common/utils";
|
||||
|
||||
export type ForwardedPortStatus = "Active" | "Disabled";
|
||||
export interface ForwardedPort {
|
||||
clusterId?: string;
|
||||
kind: string;
|
||||
namespace: string;
|
||||
name: string;
|
||||
port: number;
|
||||
forwardPort: number;
|
||||
protocol?: string;
|
||||
status?: ForwardedPortStatus;
|
||||
}
|
||||
|
||||
export class PortForwardItem implements ItemObject {
|
||||
clusterId: string;
|
||||
kind: string;
|
||||
namespace: string;
|
||||
name: string;
|
||||
port: number;
|
||||
forwardPort: number;
|
||||
protocol: string;
|
||||
status: ForwardedPortStatus;
|
||||
|
||||
constructor(pf: ForwardedPort) {
|
||||
this.clusterId = pf.clusterId;
|
||||
this.kind = pf.kind;
|
||||
this.namespace = pf.namespace;
|
||||
this.name = pf.name;
|
||||
this.port = pf.port;
|
||||
this.forwardPort = pf.forwardPort;
|
||||
this.protocol = pf.protocol ?? "http";
|
||||
this.status = pf.status ?? "Active";
|
||||
|
||||
autoBind(this);
|
||||
}
|
||||
@@ -62,12 +63,8 @@ export class PortForwardItem implements ItemObject {
|
||||
return this.namespace;
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this.forwardPort;
|
||||
}
|
||||
|
||||
getId() {
|
||||
return String(this.forwardPort);
|
||||
return `${this.namespace}-${this.kind}-${this.name}:${this.port}`;
|
||||
}
|
||||
|
||||
getKind() {
|
||||
@@ -87,16 +84,17 @@ export class PortForwardItem implements ItemObject {
|
||||
}
|
||||
|
||||
getStatus() {
|
||||
return "Active"; // to-do allow port-forward-items to be stopped (without removing them)
|
||||
return this.status;
|
||||
}
|
||||
|
||||
getSearchFields() {
|
||||
return [
|
||||
this.name,
|
||||
this.id,
|
||||
this.namespace,
|
||||
this.kind,
|
||||
this.port,
|
||||
this.forwardPort,
|
||||
this.status,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -56,3 +56,34 @@ export function aboutPortForwarding() {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function notifyErrorPortForwarding(msg: string) {
|
||||
const notificationId = `port-forward-error-notification-${getHostedClusterId()}`;
|
||||
|
||||
Notifications.error(
|
||||
(
|
||||
<div className="flex column gaps">
|
||||
<b>Port Forwarding</b>
|
||||
<p>
|
||||
{msg}
|
||||
</p>
|
||||
<div className="flex gaps row align-left box grow">
|
||||
<Button
|
||||
active
|
||||
outlined
|
||||
label="Check Port Forwarding"
|
||||
onClick={() => {
|
||||
navigate(portForwardsURL());
|
||||
notificationsStore.remove(notificationId);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
id: notificationId,
|
||||
timeout: 10_000,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import portForwardStoreInjectable from "../port-forward-store.injectable";
|
||||
|
||||
const addPortForwardInjectable = getInjectable({
|
||||
instantiate: (di) => di.inject(portForwardStoreInjectable).add,
|
||||
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
});
|
||||
|
||||
export default addPortForwardInjectable;
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import portForwardStoreInjectable from "../port-forward-store.injectable";
|
||||
|
||||
const modifyPortForwardInjectable = getInjectable({
|
||||
instantiate: (di) => di.inject(portForwardStoreInjectable).modify,
|
||||
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
});
|
||||
|
||||
export default modifyPortForwardInjectable;
|
||||
@@ -19,21 +19,21 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
import { makeObservable, observable, reaction } from "mobx";
|
||||
import { action, makeObservable, observable, reaction } from "mobx";
|
||||
import { ItemStore } from "../../../common/item.store";
|
||||
import { autoBind, disposer, getHostedClusterId, StorageHelper } from "../../utils";
|
||||
import { autoBind, disposer, StorageHelper } from "../../utils";
|
||||
import { ForwardedPort, PortForwardItem } from "../port-forward-item";
|
||||
import { notifyErrorPortForwarding } from "../port-forward-notify";
|
||||
import { apiBase } from "../../api";
|
||||
import logger from "../../../common/logger";
|
||||
import { waitUntilFree } from "tcp-port-used";
|
||||
import logger from "../../../common/logger";
|
||||
|
||||
interface Dependencies {
|
||||
storage: StorageHelper<ForwardedPort[] | undefined>
|
||||
}
|
||||
|
||||
export class PortForwardStore extends ItemStore<PortForwardItem> {
|
||||
@observable portForwards: PortForwardItem[];
|
||||
@observable portForwards: PortForwardItem[] = [];
|
||||
|
||||
constructor(private dependencies: Dependencies) {
|
||||
super();
|
||||
@@ -50,33 +50,58 @@ export class PortForwardStore extends ItemStore<PortForwardItem> {
|
||||
|
||||
if (Array.isArray(savedPortForwards)) {
|
||||
logger.info("[PORT-FORWARD-STORE] starting saved port-forwards");
|
||||
await Promise.all(savedPortForwards.map(this.add));
|
||||
|
||||
// add the disabled ones
|
||||
await Promise.all(
|
||||
savedPortForwards
|
||||
.filter((pf) => pf.status === "Disabled")
|
||||
.map(this.add),
|
||||
);
|
||||
|
||||
// add the active ones and check if they started successfully
|
||||
const results = await Promise.allSettled(
|
||||
savedPortForwards
|
||||
.filter((pf) => pf.status === "Active")
|
||||
.map(this.add),
|
||||
);
|
||||
|
||||
for (const result of results) {
|
||||
if (
|
||||
result.status === "rejected" ||
|
||||
result.value.status === "Disabled"
|
||||
) {
|
||||
notifyErrorPortForwarding(
|
||||
"One or more port-forwards could not be started",
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch() {
|
||||
return disposer(
|
||||
reaction(() => this.portForwards, () => this.loadAll()),
|
||||
reaction(
|
||||
() => this.portForwards.slice(),
|
||||
() => this.loadAll(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
loadAll() {
|
||||
return this.loadItems(async () => {
|
||||
const portForwards = await getPortForwards(getHostedClusterId());
|
||||
return this.loadItems(() => {
|
||||
const portForwards = this.getPortForwards();
|
||||
|
||||
this.dependencies.storage.set(portForwards);
|
||||
|
||||
this.reset();
|
||||
portForwards.map(pf => this.portForwards.push(new PortForwardItem(pf)));
|
||||
this.portForwards = [];
|
||||
portForwards.map((pf) => this.portForwards.push(new PortForwardItem(pf)));
|
||||
|
||||
return this.portForwards;
|
||||
});
|
||||
}
|
||||
|
||||
reset = () => {
|
||||
this.portForwards = [];
|
||||
};
|
||||
|
||||
async removeSelectedItems() {
|
||||
return Promise.all(this.selectedItems.map(this.remove));
|
||||
}
|
||||
@@ -91,87 +116,285 @@ export class PortForwardStore extends ItemStore<PortForwardItem> {
|
||||
return this.getItems()[index];
|
||||
}
|
||||
|
||||
add = async (portForward: ForwardedPort): Promise<number> => {
|
||||
/**
|
||||
* add a port-forward to the store and optionally start it
|
||||
* @param portForward the port-forward to add. If the port-forward already exists in the store it will be
|
||||
* returned with its current state. If the forwardPort field is 0 then an arbitrary port will be
|
||||
* used. If the status field is "Active" or not present then an attempt is made to start the port-forward.
|
||||
*
|
||||
* @returns the port-forward with updated status ("Active" if successfully started, "Disabled" otherwise) and
|
||||
* forwardPort
|
||||
*/
|
||||
add = action(async (portForward: ForwardedPort): Promise<ForwardedPort> => {
|
||||
const pf = this.findPortForward(portForward);
|
||||
|
||||
if (pf) {
|
||||
return pf;
|
||||
}
|
||||
|
||||
this.portForwards.push(new PortForwardItem(portForward));
|
||||
|
||||
if (!portForward.status) {
|
||||
portForward.status = "Active";
|
||||
}
|
||||
|
||||
if (portForward.status === "Active") {
|
||||
portForward = await this.start(portForward);
|
||||
}
|
||||
|
||||
return portForward;
|
||||
});
|
||||
|
||||
/**
|
||||
* modifies a port-forward in the store, including the forwardPort and protocol
|
||||
* @param portForward the port-forward to modify.
|
||||
*
|
||||
* @returns the port-forward after being modified.
|
||||
*/
|
||||
modify = action(
|
||||
async (
|
||||
portForward: ForwardedPort,
|
||||
desiredPort: number,
|
||||
): Promise<ForwardedPort> => {
|
||||
const pf = this.findPortForward(portForward);
|
||||
|
||||
if (!pf) {
|
||||
throw new Error("port-forward not found");
|
||||
}
|
||||
|
||||
if (pf.status === "Active") {
|
||||
try {
|
||||
await this.stop(pf);
|
||||
} catch {
|
||||
// ignore, assume it is stopped and proceed to restart it
|
||||
}
|
||||
|
||||
pf.forwardPort = desiredPort;
|
||||
pf.protocol = portForward.protocol ?? "http";
|
||||
this.setPortForward(pf);
|
||||
|
||||
return await this.start(pf);
|
||||
}
|
||||
|
||||
pf.forwardPort = desiredPort;
|
||||
this.setPortForward(pf);
|
||||
|
||||
return pf as ForwardedPort;
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* remove and stop an existing port-forward.
|
||||
* @param portForward the port-forward to remove.
|
||||
*/
|
||||
remove = action(async (portForward: ForwardedPort) => {
|
||||
const pf = this.findPortForward(portForward);
|
||||
|
||||
if (!pf) {
|
||||
const error = new Error("port-forward not found");
|
||||
|
||||
logger.warn(
|
||||
`[PORT-FORWARD-STORE] Error getting port-forward: ${error}`,
|
||||
portForward,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.stop(portForward);
|
||||
} catch (error) {
|
||||
if (pf.status === "Active") {
|
||||
logger.warn(
|
||||
`[PORT-FORWARD-STORE] Error removing port-forward: ${error}`,
|
||||
portForward,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const index = this.portForwards.findIndex(portForwardsEqual(portForward));
|
||||
|
||||
if (index >= 0) {
|
||||
this.portForwards.splice(index, 1);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* gets the list of port-forwards in the store
|
||||
*
|
||||
* @returns the port-forwards
|
||||
*/
|
||||
getPortForwards = (): ForwardedPort[] => {
|
||||
return this.portForwards;
|
||||
};
|
||||
|
||||
/**
|
||||
* stop an existing port-forward. Its status is set to "Disabled" after successfully stopped.
|
||||
* @param portForward the port-forward to stop.
|
||||
*
|
||||
* @throws if the port-forward could not be stopped. Its status is unchanged
|
||||
*/
|
||||
stop = action(async (portForward: ForwardedPort) => {
|
||||
const pf = this.findPortForward(portForward);
|
||||
|
||||
if (!pf) {
|
||||
logger.warn(
|
||||
"[PORT-FORWARD-STORE] Error getting port-forward: port-forward not found",
|
||||
portForward,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const { port, forwardPort } = portForward;
|
||||
|
||||
try {
|
||||
await apiBase.del(
|
||||
`/pods/port-forward/${portForward.namespace}/${portForward.kind}/${portForward.name}`,
|
||||
{ query: { port, forwardPort }},
|
||||
);
|
||||
await waitUntilFree(+forwardPort, 200, 1000);
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
`[PORT-FORWARD-STORE] Error stopping active port-forward: ${error}`,
|
||||
portForward,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
|
||||
pf.status = "Disabled";
|
||||
|
||||
this.setPortForward(pf);
|
||||
});
|
||||
|
||||
private findPortForward = (portForward: ForwardedPort) => {
|
||||
return this.portForwards.find(portForwardsEqual(portForward));
|
||||
};
|
||||
|
||||
private setPortForward = action((portForward: ForwardedPort) => {
|
||||
const index = this.portForwards.findIndex(portForwardsEqual(portForward));
|
||||
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.portForwards[index] = new PortForwardItem(portForward);
|
||||
});
|
||||
|
||||
/**
|
||||
* start an existing port-forward
|
||||
* @param portForward the port-forward to start. If the forwardPort field is 0 then an arbitrary port will be
|
||||
* used
|
||||
*
|
||||
* @returns the port-forward with updated status ("Active" if successfully started, "Disabled" otherwise) and
|
||||
* forwardPort
|
||||
*
|
||||
* @throws if the port-forward does not already exist in the store
|
||||
*/
|
||||
start = action(async (portForward: ForwardedPort): Promise<ForwardedPort> => {
|
||||
const pf = this.findPortForward(portForward);
|
||||
|
||||
if (!pf) {
|
||||
throw new Error("cannot start non-existent port-forward");
|
||||
}
|
||||
|
||||
const { port, forwardPort } = pf;
|
||||
let response: PortForwardResult;
|
||||
|
||||
try {
|
||||
const protocol = portForward.protocol ?? "http";
|
||||
const protocol = pf.protocol ?? "http";
|
||||
|
||||
response = await apiBase.post<PortForwardResult>(`/pods/port-forward/${portForward.namespace}/${portForward.kind}/${portForward.name}`, { query: { port, forwardPort, protocol }});
|
||||
response = await apiBase.post<PortForwardResult>(
|
||||
`/pods/port-forward/${pf.namespace}/${pf.kind}/${pf.name}`,
|
||||
{ query: { port, forwardPort, protocol }},
|
||||
);
|
||||
|
||||
// expecting the received port to be the specified port, unless the specified port is 0, which indicates any available port is suitable
|
||||
if (portForward.forwardPort && response?.port && response.port != +portForward.forwardPort) {
|
||||
logger.warn(`[PORT-FORWARD-STORE] specified ${portForward.forwardPort} got ${response.port}`);
|
||||
if (
|
||||
pf.forwardPort &&
|
||||
response?.port &&
|
||||
response.port != +pf.forwardPort
|
||||
) {
|
||||
logger.warn(
|
||||
`[PORT-FORWARD-STORE] specified ${pf.forwardPort}, got ${response.port}`,
|
||||
);
|
||||
}
|
||||
|
||||
pf.forwardPort = response.port;
|
||||
pf.status = "Active";
|
||||
} catch (error) {
|
||||
logger.warn("[PORT-FORWARD-STORE] Error adding port-forward:", error, portForward);
|
||||
throw (error);
|
||||
logger.warn(
|
||||
`[PORT-FORWARD-STORE] Error starting port-forward: ${error}`,
|
||||
pf,
|
||||
);
|
||||
pf.status = "Disabled";
|
||||
}
|
||||
|
||||
this.reset();
|
||||
this.setPortForward(pf);
|
||||
|
||||
return response?.port;
|
||||
};
|
||||
return pf as ForwardedPort;
|
||||
});
|
||||
|
||||
remove = async (portForward: ForwardedPort) => {
|
||||
const { port, forwardPort } = portForward;
|
||||
/**
|
||||
* get a port-forward from the store, with up-to-date status
|
||||
* @param portForward the port-forward to get.
|
||||
*
|
||||
* @returns the port-forward with updated status ("Active" if running, "Disabled" if not) and
|
||||
* forwardPort used.
|
||||
*
|
||||
* @throws if the port-forward does not exist in the store
|
||||
*/
|
||||
getPortForward = async (
|
||||
portForward: ForwardedPort,
|
||||
): Promise<ForwardedPort> => {
|
||||
if (!this.findPortForward(portForward)) {
|
||||
throw new Error("port-forward not found");
|
||||
}
|
||||
|
||||
let pf: ForwardedPort;
|
||||
|
||||
try {
|
||||
await apiBase.del(`/pods/port-forward/${portForward.namespace}/${portForward.kind}/${portForward.name}`, { query: { port, forwardPort }});
|
||||
await waitUntilFree(+forwardPort, 200, 1000);
|
||||
// check if the port-forward is active, and if so check if it has the same local port
|
||||
pf = await getActivePortForward(portForward);
|
||||
|
||||
if (pf.forwardPort && pf.forwardPort !== portForward.forwardPort) {
|
||||
logger.warn(
|
||||
`[PORT-FORWARD-STORE] local port, expected ${pf.forwardPort}, got ${portForward.forwardPort}`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn("[PORT-FORWARD-STORE] Error removing port-forward:", error, portForward);
|
||||
throw (error);
|
||||
// port is not active
|
||||
}
|
||||
|
||||
this.reset();
|
||||
};
|
||||
|
||||
modify = async (portForward: ForwardedPort, desiredPort: number): Promise<number> => {
|
||||
await this.remove(portForward);
|
||||
|
||||
portForward.forwardPort = desiredPort;
|
||||
|
||||
const port = await this.add(portForward);
|
||||
|
||||
this.reset();
|
||||
|
||||
return port;
|
||||
return pf;
|
||||
};
|
||||
}
|
||||
|
||||
export interface PortForwardResult {
|
||||
interface PortForwardResult {
|
||||
port: number;
|
||||
}
|
||||
|
||||
interface PortForwardsResult {
|
||||
portForwards: ForwardedPort[];
|
||||
function portForwardsEqual(portForward: ForwardedPort) {
|
||||
return (pf: ForwardedPort) => (
|
||||
pf.kind == portForward.kind &&
|
||||
pf.name == portForward.name &&
|
||||
pf.namespace == portForward.namespace &&
|
||||
pf.port == portForward.port
|
||||
);
|
||||
}
|
||||
|
||||
export async function getPortForward(portForward: ForwardedPort): Promise<number> {
|
||||
async function getActivePortForward(portForward: ForwardedPort): Promise<ForwardedPort> {
|
||||
const { port, forwardPort, protocol } = portForward;
|
||||
let response: PortForwardResult;
|
||||
|
||||
try {
|
||||
response = await apiBase.get<PortForwardResult>(`/pods/port-forward/${portForward.namespace}/${portForward.kind}/${portForward.name}`, { query: { port, forwardPort, protocol }});
|
||||
} catch (error) {
|
||||
logger.warn("[PORT-FORWARD-STORE] Error getting port-forward:", error, portForward);
|
||||
throw (error);
|
||||
logger.warn(`[PORT-FORWARD-STORE] Error getting active port-forward: ${error}`, portForward);
|
||||
}
|
||||
|
||||
return response?.port;
|
||||
}
|
||||
portForward.status = response?.port ? "Active" : "Disabled";
|
||||
portForward.forwardPort = response?.port;
|
||||
|
||||
|
||||
export async function getPortForwards(clusterId?: string): Promise<ForwardedPort[]> {
|
||||
try {
|
||||
const response = await apiBase.get<PortForwardsResult>("/pods/port-forwards", { query: { clusterId }});
|
||||
|
||||
return response.portForwards;
|
||||
} catch (error) {
|
||||
logger.warn("[PORT-FORWARD-STORE] Error getting all port-forwards:", error);
|
||||
|
||||
return [];
|
||||
}
|
||||
return portForward;
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import portForwardStoreInjectable from "../port-forward-store.injectable";
|
||||
|
||||
const removePortForwardInjectable = getInjectable({
|
||||
instantiate: (di) => di.inject(portForwardStoreInjectable).remove,
|
||||
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
});
|
||||
|
||||
export default removePortForwardInjectable;
|
||||
@@ -35,7 +35,6 @@ export function openPortForward(portForward: ForwardedPort) {
|
||||
openExternal(browseTo)
|
||||
.catch(error => {
|
||||
logger.error(`failed to open in browser: ${error}`, {
|
||||
clusterId: portForward.clusterId,
|
||||
port: portForward.port,
|
||||
kind: portForward.kind,
|
||||
namespace: portForward.namespace,
|
||||
|
||||
Reference in new issue
Block a user