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:
Janne Savolainen committed 2022-01-11 13:56:23 +02:00
commit 8d87bd0985
80 files changed
+1822 -1271

No files matched your search

@@ -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;
@@ -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;
}
@@ -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;