|
|
@@ -1,4 +1,11 @@
|
|
|
-import React, { useContext, useState, useEffect, useRef } from "react";
|
|
|
+import React, {
|
|
|
+ useContext,
|
|
|
+ useState,
|
|
|
+ useEffect,
|
|
|
+ useRef,
|
|
|
+ useCallback,
|
|
|
+ useMemo,
|
|
|
+} from "react";
|
|
|
import styled from "styled-components";
|
|
|
import yaml from "js-yaml";
|
|
|
import close from "assets/close.png";
|
|
|
@@ -25,10 +32,10 @@ import MetricsSection from "./metrics/MetricsSection";
|
|
|
import ListSection from "./ListSection";
|
|
|
import StatusSection from "./status/StatusSection";
|
|
|
import SettingsSection from "./SettingsSection";
|
|
|
-import ChartList from "../chart/ChartList";
|
|
|
-import { withAuth, WithAuthProps } from "shared/auth/AuthorizationHoc";
|
|
|
+import { useWebsockets } from "shared/hooks/useWebsockets";
|
|
|
+import useAuth from "shared/auth/useAuth";
|
|
|
|
|
|
-type PropsType = WithAuthProps & {
|
|
|
+type Props = {
|
|
|
namespace: string;
|
|
|
currentChart: ChartType;
|
|
|
currentCluster: ClusterType;
|
|
|
@@ -37,38 +44,23 @@ type PropsType = WithAuthProps & {
|
|
|
isMetricsInstalled: boolean;
|
|
|
};
|
|
|
|
|
|
-type StateType = {
|
|
|
- currentChart: ChartType;
|
|
|
- loading: boolean;
|
|
|
- showRevisions: boolean;
|
|
|
- components: ResourceType[];
|
|
|
- podSelectors: string[];
|
|
|
- isPreview: boolean;
|
|
|
- isUpdatingChart: boolean;
|
|
|
- devOpsMode: boolean;
|
|
|
- tabOptions: any[];
|
|
|
- saveValuesStatus: string | null;
|
|
|
- forceRefreshRevisions: boolean; // Update revisions after upgrading values
|
|
|
- controllers: Record<string, Record<string, any>>;
|
|
|
- websockets: Record<string, any>;
|
|
|
- url: string | null;
|
|
|
- showDeleteOverlay: boolean;
|
|
|
- deleting: boolean;
|
|
|
- formData: any;
|
|
|
- imageIsPlaceholder: boolean;
|
|
|
- newestImage: string;
|
|
|
+const getReadableDate = (s: string) => {
|
|
|
+ let ts = new Date(s);
|
|
|
+ let date = ts.toLocaleDateString();
|
|
|
+ let time = ts.toLocaleTimeString([], {
|
|
|
+ hour: "numeric",
|
|
|
+ minute: "2-digit",
|
|
|
+ });
|
|
|
+ return `${time} on ${date}`;
|
|
|
};
|
|
|
|
|
|
-const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
+const ExpandedChart: React.FC<Props> = (props) => {
|
|
|
const [currentChart, setCurrentChart] = useState<ChartType>(
|
|
|
props.currentChart
|
|
|
);
|
|
|
- const [loading, setLoading] = useState<boolean>(true);
|
|
|
const [showRevisions, setShowRevisions] = useState<boolean>(false);
|
|
|
const [components, setComponents] = useState<ResourceType[]>([]);
|
|
|
- const [podSelectors, setPodSelectors] = useState<string[]>([]);
|
|
|
const [isPreview, setIsPreview] = useState<boolean>(false);
|
|
|
- const [isUpdatingChart, setIsUpdatingChart] = useState<boolean>(false);
|
|
|
const [devOpsMode, setDevOpsMode] = useState<boolean>(
|
|
|
localStorage.getItem("devOpsMode") === "true"
|
|
|
);
|
|
|
@@ -80,150 +72,128 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
const [controllers, setControllers] = useState<
|
|
|
Record<string, Record<string, any>>
|
|
|
>({});
|
|
|
- const controllersCallback = useRef(null);
|
|
|
- const [websockets, setWebsockets] = useState<Record<string, any>>({});
|
|
|
const [url, setUrl] = useState<string>(null);
|
|
|
const [showDeleteOverlay, setShowDeleteOverlay] = useState<boolean>(false);
|
|
|
const [deleting, setDeleting] = useState<boolean>(false);
|
|
|
- const [formData, setFormData] = useState<any>({});
|
|
|
const [imageIsPlaceholder, setImageIsPlaceholer] = useState<boolean>(false);
|
|
|
const [newestImage, setNewestImage] = useState<string>(null);
|
|
|
|
|
|
+ const [isAuthorized] = useAuth();
|
|
|
+
|
|
|
+ const {
|
|
|
+ newWebsocket,
|
|
|
+ openWebsocket,
|
|
|
+ closeAllWebsockets,
|
|
|
+ closeWebsocket,
|
|
|
+ } = useWebsockets();
|
|
|
+
|
|
|
const { currentCluster, currentProject, setCurrentError } = useContext(
|
|
|
Context
|
|
|
);
|
|
|
|
|
|
// Retrieve full chart data (includes form and values)
|
|
|
- const getChartData = (chart: ChartType) => {
|
|
|
- let { currentCluster, currentChart } = props;
|
|
|
+ const getChartData = async (chart: ChartType) => {
|
|
|
+ const res = await api.getChart(
|
|
|
+ "<token>",
|
|
|
+ {
|
|
|
+ namespace: chart.namespace,
|
|
|
+ cluster_id: currentCluster.id,
|
|
|
+ storage: StorageType.Secret,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: chart.name,
|
|
|
+ revision: chart.version,
|
|
|
+ id: currentProject.id,
|
|
|
+ }
|
|
|
+ );
|
|
|
+ const image = res.data?.config?.image?.repository;
|
|
|
+ const tag = res.data?.config?.image?.tag?.toString();
|
|
|
+ const newNewestImage = tag ? image + ":" + tag : image;
|
|
|
+ let imageIsPlaceholder = false;
|
|
|
+ if (
|
|
|
+ (image === "porterdev/hello-porter" ||
|
|
|
+ image === "public.ecr.aws/o1j4x7p4/hello-porter") &&
|
|
|
+ !newestImage
|
|
|
+ ) {
|
|
|
+ imageIsPlaceholder = true;
|
|
|
+ }
|
|
|
+ setCurrentChart(res.data);
|
|
|
+ setImageIsPlaceholer(imageIsPlaceholder);
|
|
|
+ setNewestImage(newNewestImage);
|
|
|
|
|
|
- setLoading(true);
|
|
|
- api
|
|
|
- .getChart(
|
|
|
+ updateComponents(res.data);
|
|
|
+ };
|
|
|
+
|
|
|
+ const getControllers = async (chart: ChartType) => {
|
|
|
+ // don't retrieve controllers for chart that failed to even deploy.
|
|
|
+ if (chart.info.status == "failed") return;
|
|
|
+
|
|
|
+ try {
|
|
|
+ const { data: chartControllers } = await api.getChartControllers(
|
|
|
"<token>",
|
|
|
{
|
|
|
- namespace: currentChart.namespace,
|
|
|
+ namespace: chart.namespace,
|
|
|
cluster_id: currentCluster.id,
|
|
|
storage: StorageType.Secret,
|
|
|
},
|
|
|
{
|
|
|
+ id: currentProject.id,
|
|
|
name: chart.name,
|
|
|
revision: chart.version,
|
|
|
- id: currentProject.id,
|
|
|
}
|
|
|
- )
|
|
|
- .then((res) => {
|
|
|
- let image = res.data?.config?.image?.repository;
|
|
|
- let tag = res.data?.config?.image?.tag?.toString();
|
|
|
- let newNewestImage = tag ? image + ":" + tag : image;
|
|
|
- let imageIsPlaceholder = false;
|
|
|
- if (
|
|
|
- (image === "porterdev/hello-porter" ||
|
|
|
- image === "public.ecr.aws/o1j4x7p4/hello-porter") &&
|
|
|
- !newestImage
|
|
|
- ) {
|
|
|
- imageIsPlaceholder = true;
|
|
|
- }
|
|
|
- updateComponents(
|
|
|
- {
|
|
|
- currentChart: res.data,
|
|
|
- loading: false,
|
|
|
- imageIsPlaceholder,
|
|
|
- newNewestImage,
|
|
|
- },
|
|
|
- res.data
|
|
|
- );
|
|
|
- })
|
|
|
- .catch(console.log);
|
|
|
- };
|
|
|
-
|
|
|
- const getControllers = async (chart: ChartType) => {
|
|
|
- // don't retrieve controllers for chart that failed to even deploy.
|
|
|
- if (chart.info.status == "failed") return;
|
|
|
-
|
|
|
- // TODO: properly promisify
|
|
|
- await new Promise((next: (res?: any) => void) => {
|
|
|
- api
|
|
|
- .getChartControllers(
|
|
|
- "<token>",
|
|
|
- {
|
|
|
- namespace: chart.namespace,
|
|
|
- cluster_id: currentCluster.id,
|
|
|
- storage: StorageType.Secret,
|
|
|
- },
|
|
|
- {
|
|
|
- id: currentProject.id,
|
|
|
- name: chart.name,
|
|
|
- revision: chart.version,
|
|
|
- }
|
|
|
- )
|
|
|
- .then((res) => {
|
|
|
- res.data?.forEach(async (c: any) => {
|
|
|
- await new Promise((nextController: (res?: any) => void) => {
|
|
|
- c.metadata.kind = c.kind;
|
|
|
- controllersCallback.current = nextController;
|
|
|
- setControllers({
|
|
|
- ...controllers,
|
|
|
- [c.metadata.uid]: c,
|
|
|
- });
|
|
|
- });
|
|
|
- });
|
|
|
- next();
|
|
|
- })
|
|
|
- .catch((err) => setCurrentError(JSON.stringify(err)));
|
|
|
- });
|
|
|
- };
|
|
|
+ );
|
|
|
|
|
|
- const setupWebsocket = (kind: string, chart: ChartType) => {
|
|
|
- let protocol = window.location.protocol == "https:" ? "wss" : "ws";
|
|
|
- let ws = new WebSocket(
|
|
|
- `${protocol}://${window.location.host}/api/projects/${currentProject.id}/k8s/${kind}/status?cluster_id=${currentCluster.id}`
|
|
|
- );
|
|
|
- ws.onopen = () => {
|
|
|
- console.log("connected to websocket");
|
|
|
- };
|
|
|
+ chartControllers.forEach((c: any) => {
|
|
|
+ c.metadata.kind = c.kind;
|
|
|
|
|
|
- ws.onmessage = (evt: MessageEvent) => {
|
|
|
- let event = JSON.parse(evt.data);
|
|
|
+ setControllers((oldControllers) => ({
|
|
|
+ ...oldControllers,
|
|
|
+ [c.metadata.kind]: c,
|
|
|
+ }));
|
|
|
+ });
|
|
|
|
|
|
- if (event.event_type == "UPDATE") {
|
|
|
- let object = event.Object;
|
|
|
- object.metadata.kind = event.Kind;
|
|
|
+ return;
|
|
|
+ } catch (error) {
|
|
|
+ if (typeof error !== "string") {
|
|
|
+ setCurrentError(JSON.stringify(error));
|
|
|
+ }
|
|
|
+ setCurrentError(error);
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
- if (!controllers[object.metadata.uid]) return;
|
|
|
+ const setupWebsocket = (kind: string) => {
|
|
|
+ const apiEndpoint = `/api/projects/${currentProject.id}/k8s/${kind}/status?cluster_id=${currentCluster.id}`;
|
|
|
|
|
|
- setControllers({
|
|
|
- ...controllers,
|
|
|
- [object.metadata.uid]: object,
|
|
|
- });
|
|
|
- }
|
|
|
- };
|
|
|
+ const wsConfig = {
|
|
|
+ onmessage(evt: MessageEvent) {
|
|
|
+ const event = JSON.parse(evt.data);
|
|
|
|
|
|
- ws.onclose = () => {
|
|
|
- console.log("closing websocket");
|
|
|
- };
|
|
|
+ if (event.event_type == "UPDATE") {
|
|
|
+ let object = event.Object;
|
|
|
+ object.metadata.kind = event.Kind;
|
|
|
|
|
|
- ws.onerror = (err: ErrorEvent) => {
|
|
|
- console.log(err);
|
|
|
- ws.close();
|
|
|
+ setControllers((oldControllers) => {
|
|
|
+ if (oldControllers[object.metadata.uid]) {
|
|
|
+ return oldControllers;
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ ...oldControllers,
|
|
|
+ [object.metadata.uid]: object,
|
|
|
+ };
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onerror() {
|
|
|
+ closeWebsocket(kind);
|
|
|
+ },
|
|
|
};
|
|
|
|
|
|
- return ws;
|
|
|
+ newWebsocket(kind, apiEndpoint, wsConfig);
|
|
|
};
|
|
|
|
|
|
- const setControllerWebsockets = (
|
|
|
- controller_types: any[],
|
|
|
- chart: ChartType
|
|
|
- ) => {
|
|
|
- let websockets = controller_types.map((kind: string) => {
|
|
|
- return setupWebsocket(kind, chart);
|
|
|
- });
|
|
|
- setWebsockets(websockets);
|
|
|
- };
|
|
|
-
|
|
|
- const updateComponents = (state: any, currentChart: ChartType) => {
|
|
|
- api
|
|
|
- .getChartComponents(
|
|
|
+ const updateComponents = async (currentChart: ChartType) => {
|
|
|
+ try {
|
|
|
+ const res = await api.getChartComponents(
|
|
|
"<token>",
|
|
|
{
|
|
|
namespace: currentChart.namespace,
|
|
|
@@ -235,18 +205,14 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
name: currentChart.name,
|
|
|
revision: currentChart.version,
|
|
|
}
|
|
|
- )
|
|
|
- .then((res) => {
|
|
|
- setComponents(res.data.Objects);
|
|
|
- setPodSelectors(res.data.PodSelectors);
|
|
|
- updateTabs();
|
|
|
- })
|
|
|
- .catch(console.log);
|
|
|
+ );
|
|
|
+ setComponents(res.data.Objects);
|
|
|
+ } catch (error) {
|
|
|
+ console.log(error);
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
- const refreshChart = () => getChartData(currentChart);
|
|
|
-
|
|
|
- const onSubmit = (rawValues: any) => {
|
|
|
+ const onSubmit = async (rawValues: any) => {
|
|
|
// Convert dotted keys to nested objects
|
|
|
let values = {};
|
|
|
|
|
|
@@ -269,10 +235,9 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
});
|
|
|
|
|
|
setSaveValueStatus("loading");
|
|
|
- refreshChart();
|
|
|
-
|
|
|
- api
|
|
|
- .upgradeChartValues(
|
|
|
+ getChartData(currentChart);
|
|
|
+ try {
|
|
|
+ await api.upgradeChartValues(
|
|
|
"<token>",
|
|
|
{
|
|
|
namespace: currentChart.namespace,
|
|
|
@@ -284,63 +249,62 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
name: currentChart.name,
|
|
|
cluster_id: currentCluster.id,
|
|
|
}
|
|
|
- )
|
|
|
- .then(() => {
|
|
|
- setSaveValueStatus("successful");
|
|
|
- setForceRefreshRevisions(true);
|
|
|
+ );
|
|
|
|
|
|
- window.analytics.track("Chart Upgraded", {
|
|
|
- chart: currentChart.name,
|
|
|
- values: valuesYaml,
|
|
|
- });
|
|
|
- })
|
|
|
- .catch((err) => {
|
|
|
- let parsedErr =
|
|
|
- err?.response?.data?.errors && err.response.data.errors[0];
|
|
|
+ setSaveValueStatus("successful");
|
|
|
+ setForceRefreshRevisions(true);
|
|
|
|
|
|
- if (parsedErr) {
|
|
|
- err = parsedErr;
|
|
|
- }
|
|
|
+ window.analytics.track("Chart Upgraded", {
|
|
|
+ chart: currentChart.name,
|
|
|
+ values: valuesYaml,
|
|
|
+ });
|
|
|
+ } catch (err) {
|
|
|
+ const parsedErr =
|
|
|
+ err?.response?.data?.errors && err.response.data.errors[0];
|
|
|
|
|
|
- setSaveValueStatus(err);
|
|
|
+ if (parsedErr) {
|
|
|
+ err = parsedErr;
|
|
|
+ }
|
|
|
|
|
|
- setCurrentError(parsedErr);
|
|
|
+ setSaveValueStatus(err);
|
|
|
|
|
|
- window.analytics.track("Failed to Upgrade Chart", {
|
|
|
- chart: currentChart.name,
|
|
|
- values: valuesYaml,
|
|
|
- error: err,
|
|
|
- });
|
|
|
+ setCurrentError(parsedErr);
|
|
|
+
|
|
|
+ window.analytics.track("Failed to Upgrade Chart", {
|
|
|
+ chart: currentChart.name,
|
|
|
+ values: valuesYaml,
|
|
|
+ error: err,
|
|
|
});
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
- const handleUpgradeVersion = (version: string, cb: () => void) => {
|
|
|
- // convert current values to yaml
|
|
|
- let values = currentChart.config;
|
|
|
+ const handleUpgradeVersion = useCallback(
|
|
|
+ async (version: string, cb: () => void) => {
|
|
|
+ // convert current values to yaml
|
|
|
+ let values = currentChart.config;
|
|
|
|
|
|
- let valuesYaml = yaml.dump({
|
|
|
- ...values,
|
|
|
- });
|
|
|
+ let valuesYaml = yaml.dump({
|
|
|
+ ...values,
|
|
|
+ });
|
|
|
|
|
|
- setSaveValueStatus("loading");
|
|
|
- refreshChart();
|
|
|
+ setSaveValueStatus("loading");
|
|
|
+ getChartData(currentChart);
|
|
|
|
|
|
- api
|
|
|
- .upgradeChartValues(
|
|
|
- "<token>",
|
|
|
- {
|
|
|
- namespace: currentChart.namespace,
|
|
|
- storage: StorageType.Secret,
|
|
|
- values: valuesYaml,
|
|
|
- version: version,
|
|
|
- },
|
|
|
- {
|
|
|
- id: currentProject.id,
|
|
|
- name: currentChart.name,
|
|
|
- cluster_id: currentCluster.id,
|
|
|
- }
|
|
|
- )
|
|
|
- .then(() => {
|
|
|
+ try {
|
|
|
+ await api.upgradeChartValues(
|
|
|
+ "<token>",
|
|
|
+ {
|
|
|
+ namespace: currentChart.namespace,
|
|
|
+ storage: StorageType.Secret,
|
|
|
+ values: valuesYaml,
|
|
|
+ version: version,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: currentProject.id,
|
|
|
+ name: currentChart.name,
|
|
|
+ cluster_id: currentCluster.id,
|
|
|
+ }
|
|
|
+ );
|
|
|
setSaveValueStatus("successful");
|
|
|
setForceRefreshRevisions(true);
|
|
|
|
|
|
@@ -350,8 +314,7 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
});
|
|
|
|
|
|
cb && cb();
|
|
|
- })
|
|
|
- .catch((err) => {
|
|
|
+ } catch (err) {
|
|
|
let parsedErr =
|
|
|
err?.response?.data?.errors && err.response.data.errors[0];
|
|
|
|
|
|
@@ -360,7 +323,6 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
}
|
|
|
|
|
|
setSaveValueStatus(err);
|
|
|
- setLoading(false);
|
|
|
setCurrentError(parsedErr);
|
|
|
|
|
|
window.analytics.track("Failed to Upgrade Chart", {
|
|
|
@@ -368,8 +330,10 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
values: valuesYaml,
|
|
|
error: err,
|
|
|
});
|
|
|
- });
|
|
|
- };
|
|
|
+ }
|
|
|
+ },
|
|
|
+ [currentChart]
|
|
|
+ );
|
|
|
|
|
|
const renderTabContents = (currentTab: string) => {
|
|
|
let { setSidebar } = props;
|
|
|
@@ -399,7 +363,7 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
return (
|
|
|
<SettingsSection
|
|
|
currentChart={chart}
|
|
|
- refreshChart={refreshChart}
|
|
|
+ refreshChart={() => getChartData(currentChart)}
|
|
|
setShowDeleteOverlay={(x: boolean) => setShowDeleteOverlay(x)}
|
|
|
/>
|
|
|
);
|
|
|
@@ -426,8 +390,8 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
return (
|
|
|
<ValuesYaml
|
|
|
currentChart={chart}
|
|
|
- refreshChart={refreshChart}
|
|
|
- disabled={!props.isAuthorized("application", "", ["get", "update"])}
|
|
|
+ refreshChart={() => getChartData(currentChart)}
|
|
|
+ disabled={!isAuthorized("application", "", ["get", "update"])}
|
|
|
/>
|
|
|
);
|
|
|
default:
|
|
|
@@ -435,11 +399,6 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
};
|
|
|
|
|
|
const updateTabs = () => {
|
|
|
- let formData = currentChart.form;
|
|
|
- if (formData) {
|
|
|
- setFormData(formData);
|
|
|
- }
|
|
|
-
|
|
|
// Collate non-form tabs
|
|
|
let tabOptions = [] as any[];
|
|
|
tabOptions.push({ label: "Status", value: "status" });
|
|
|
@@ -458,12 +417,12 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
}
|
|
|
|
|
|
// Settings tab is always last
|
|
|
- if (props.isAuthorized("application", "", ["get", "delete"])) {
|
|
|
+ if (isAuthorized("application", "", ["get", "delete"])) {
|
|
|
tabOptions.push({ label: "Settings", value: "settings" });
|
|
|
}
|
|
|
|
|
|
// Filter tabs if previewing an old revision or updating the chart version
|
|
|
- if (isPreview || isUpdatingChart) {
|
|
|
+ if (isPreview) {
|
|
|
let liveTabs = ["status", "settings", "deploy", "metrics"];
|
|
|
tabOptions = tabOptions.filter(
|
|
|
(tab: any) => !liveTabs.includes(tab.value)
|
|
|
@@ -494,17 +453,21 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- const readableDate = (s: string) => {
|
|
|
- let ts = new Date(s);
|
|
|
- let date = ts.toLocaleDateString();
|
|
|
- let time = ts.toLocaleTimeString([], {
|
|
|
- hour: "numeric",
|
|
|
- minute: "2-digit",
|
|
|
- });
|
|
|
- return `${time} on ${date}`;
|
|
|
- };
|
|
|
+ const chartStatus = useMemo(() => {
|
|
|
+ const getAvailability = (kind: string, c: any) => {
|
|
|
+ switch (kind?.toLowerCase()) {
|
|
|
+ case "deployment":
|
|
|
+ case "replicaset":
|
|
|
+ return c.status.availableReplicas == c.status.replicas;
|
|
|
+ case "statefulset":
|
|
|
+ return c.status.readyReplicas == c.status.replicas;
|
|
|
+ case "daemonset":
|
|
|
+ return c.status.numberAvailable == c.status.desiredNumberScheduled;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const chartStatus = currentChart.info.status;
|
|
|
|
|
|
- const getChartStatus = (chartStatus: string) => {
|
|
|
if (chartStatus === "deployed") {
|
|
|
for (var uid in controllers) {
|
|
|
let value = controllers[uid];
|
|
|
@@ -530,57 +493,7 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
return "deployed";
|
|
|
}
|
|
|
return chartStatus;
|
|
|
- };
|
|
|
-
|
|
|
- const getAvailability = (kind: string, c: any) => {
|
|
|
- switch (kind?.toLowerCase()) {
|
|
|
- case "deployment":
|
|
|
- case "replicaset":
|
|
|
- return c.status.availableReplicas == c.status.replicas;
|
|
|
- case "statefulset":
|
|
|
- return c.status.readyReplicas == c.status.replicas;
|
|
|
- case "daemonset":
|
|
|
- return c.status.numberAvailable == c.status.desiredNumberScheduled;
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- useEffect(() => {
|
|
|
- window.analytics.track("Opened Chart", {
|
|
|
- chart: currentChart.name,
|
|
|
- });
|
|
|
-
|
|
|
- getChartData(currentChart);
|
|
|
- getControllers(currentChart); // isn't this async?
|
|
|
- setControllerWebsockets(
|
|
|
- ["deployment", "statefulset", "daemonset", "replicaset"],
|
|
|
- currentChart
|
|
|
- );
|
|
|
-
|
|
|
- api
|
|
|
- .getChartComponents(
|
|
|
- "<token>",
|
|
|
- {
|
|
|
- namespace: currentChart.namespace,
|
|
|
- cluster_id: currentCluster.id,
|
|
|
- storage: StorageType.Secret,
|
|
|
- },
|
|
|
- {
|
|
|
- id: currentProject.id,
|
|
|
- name: currentChart.name,
|
|
|
- revision: currentChart.version,
|
|
|
- }
|
|
|
- )
|
|
|
- .then((res) => setComponents(res.data.Objects))
|
|
|
- .catch(console.log);
|
|
|
-
|
|
|
- return () => {
|
|
|
- if (websockets?.length > 0) {
|
|
|
- websockets?.forEach((ws: WebSocket) => {
|
|
|
- ws.close();
|
|
|
- });
|
|
|
- }
|
|
|
- };
|
|
|
- }, []);
|
|
|
+ }, [currentChart, controllers]);
|
|
|
|
|
|
const renderUrl = () => {
|
|
|
if (url) {
|
|
|
@@ -590,34 +503,28 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
{url}
|
|
|
</Url>
|
|
|
);
|
|
|
- } else {
|
|
|
- let serviceName = null as string;
|
|
|
- let serviceNamespace = null as string;
|
|
|
-
|
|
|
- components?.forEach((c: any) => {
|
|
|
- if (c.Kind == "Service") {
|
|
|
- serviceName = c.Name;
|
|
|
- serviceNamespace = c.Namespace;
|
|
|
- }
|
|
|
- });
|
|
|
+ }
|
|
|
|
|
|
- if (!serviceName || !serviceNamespace) {
|
|
|
- return;
|
|
|
- }
|
|
|
+ const service: any = components?.find((c) => {
|
|
|
+ return c.Kind === "Service";
|
|
|
+ });
|
|
|
|
|
|
- return (
|
|
|
- <Url>
|
|
|
- <Bolded>Internal URI:</Bolded>
|
|
|
- {`${serviceName}.${serviceNamespace}.svc.cluster.local`}
|
|
|
- </Url>
|
|
|
- );
|
|
|
+ if (!service?.Name || !service?.Namespace) {
|
|
|
+ return;
|
|
|
}
|
|
|
+
|
|
|
+ return (
|
|
|
+ <Url>
|
|
|
+ <Bolded>Internal URI:</Bolded>
|
|
|
+ {`${service.Name}.${service.Namespace}.svc.cluster.local`}
|
|
|
+ </Url>
|
|
|
+ );
|
|
|
};
|
|
|
|
|
|
- const handleUninstallChart = () => {
|
|
|
+ const handleUninstallChart = async () => {
|
|
|
setDeleting(true);
|
|
|
- api
|
|
|
- .uninstallTemplate(
|
|
|
+ try {
|
|
|
+ await api.uninstallTemplate(
|
|
|
"<token>",
|
|
|
{},
|
|
|
{
|
|
|
@@ -627,34 +534,45 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
id: currentProject.id,
|
|
|
cluster_id: currentCluster.id,
|
|
|
}
|
|
|
- )
|
|
|
- .then((res) => {
|
|
|
- setShowDeleteOverlay(false);
|
|
|
- props.closeChart();
|
|
|
- })
|
|
|
- .catch(console.log);
|
|
|
- };
|
|
|
-
|
|
|
- const renderDeleteOverlay = () => {
|
|
|
- if (deleting) {
|
|
|
- return (
|
|
|
- <DeleteOverlay>
|
|
|
- <Loading />
|
|
|
- </DeleteOverlay>
|
|
|
);
|
|
|
+ setShowDeleteOverlay(false);
|
|
|
+ props.closeChart();
|
|
|
+ } catch (error) {
|
|
|
+ console.log(error);
|
|
|
+ setCurrentError("Couldn't uninstall chart, please try again");
|
|
|
}
|
|
|
};
|
|
|
|
|
|
useEffect(() => {
|
|
|
- if (controllersCallback.current) controllersCallback.current();
|
|
|
- }, [controllers]);
|
|
|
+ window.analytics.track("Opened Chart", {
|
|
|
+ chart: currentChart.name,
|
|
|
+ });
|
|
|
+
|
|
|
+ getChartData(currentChart).then(() => {
|
|
|
+ getControllers(currentChart).then(() => {
|
|
|
+ ["deployment", "statefulset", "daemonset", "replicaset"]
|
|
|
+ .map((kind) => {
|
|
|
+ setupWebsocket(kind);
|
|
|
+ return kind;
|
|
|
+ })
|
|
|
+ .forEach((kind) => {
|
|
|
+ openWebsocket(kind);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ return () => {
|
|
|
+ closeAllWebsockets();
|
|
|
+ };
|
|
|
+ }, []);
|
|
|
|
|
|
useEffect(() => {
|
|
|
updateTabs();
|
|
|
localStorage.setItem("devOpsMode", devOpsMode.toString());
|
|
|
- }, [devOpsMode]);
|
|
|
+ }, [devOpsMode, currentChart?.form, isPreview]);
|
|
|
|
|
|
useEffect(() => {
|
|
|
+ let isSubscribed = true;
|
|
|
let ingressName = null;
|
|
|
for (var i = 0; i < components.length; i++) {
|
|
|
if (components[i].Kind === "Ingress") {
|
|
|
@@ -662,6 +580,8 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ if (!ingressName) return;
|
|
|
+
|
|
|
api
|
|
|
.getIngress(
|
|
|
"<token>",
|
|
|
@@ -675,6 +595,9 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
}
|
|
|
)
|
|
|
.then((res) => {
|
|
|
+ if (!isSubscribed) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
if (res.data?.spec?.rules && res.data?.spec?.rules[0]?.host) {
|
|
|
setUrl(`https://${res.data?.spec?.rules[0]?.host}`);
|
|
|
return;
|
|
|
@@ -688,15 +611,12 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
}
|
|
|
})
|
|
|
.catch(console.log);
|
|
|
+ return () => (isSubscribed = false);
|
|
|
}, [components]);
|
|
|
|
|
|
- let { closeChart } = props;
|
|
|
- let chart = currentChart;
|
|
|
- let status = getChartStatus(chart.info.status);
|
|
|
-
|
|
|
return (
|
|
|
<>
|
|
|
- <CloseOverlay onClick={closeChart} />
|
|
|
+ <CloseOverlay onClick={props.closeChart} />
|
|
|
<StyledExpandedChart>
|
|
|
<ConfirmOverlay
|
|
|
show={showDeleteOverlay}
|
|
|
@@ -704,35 +624,38 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
onYes={handleUninstallChart}
|
|
|
onNo={() => setShowDeleteOverlay(false)}
|
|
|
/>
|
|
|
- {renderDeleteOverlay()}
|
|
|
-
|
|
|
+ {deleting && (
|
|
|
+ <DeleteOverlay>
|
|
|
+ <Loading />
|
|
|
+ </DeleteOverlay>
|
|
|
+ )}
|
|
|
<HeaderWrapper>
|
|
|
<TitleSection>
|
|
|
<Title>
|
|
|
<IconWrapper>{renderIcon()}</IconWrapper>
|
|
|
- {chart.name}
|
|
|
+ {currentChart.name}
|
|
|
</Title>
|
|
|
- {chart.chart.metadata.name != "worker" &&
|
|
|
- chart.chart.metadata.name != "job" &&
|
|
|
+ {currentChart.chart.metadata.name != "worker" &&
|
|
|
+ currentChart.chart.metadata.name != "job" &&
|
|
|
renderUrl()}
|
|
|
<InfoWrapper>
|
|
|
<StatusIndicator
|
|
|
controllers={controllers}
|
|
|
- status={chart.info.status}
|
|
|
+ status={currentChart.info.status}
|
|
|
margin_left={"0px"}
|
|
|
/>
|
|
|
<LastDeployed>
|
|
|
<Dot>•</Dot>Last deployed
|
|
|
- {" " + readableDate(chart.info.last_deployed)}
|
|
|
+ {" " + getReadableDate(currentChart.info.last_deployed)}
|
|
|
</LastDeployed>
|
|
|
</InfoWrapper>
|
|
|
|
|
|
<TagWrapper>
|
|
|
- Namespace <NamespaceTag>{chart.namespace}</NamespaceTag>
|
|
|
+ Namespace <NamespaceTag>{currentChart.namespace}</NamespaceTag>
|
|
|
</TagWrapper>
|
|
|
</TitleSection>
|
|
|
|
|
|
- <CloseButton onClick={closeChart}>
|
|
|
+ <CloseButton onClick={props.closeChart}>
|
|
|
<CloseButtonImg src={close} />
|
|
|
</CloseButton>
|
|
|
|
|
|
@@ -741,17 +664,18 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
toggleShowRevisions={() => {
|
|
|
setShowRevisions(!showRevisions);
|
|
|
}}
|
|
|
- chart={chart}
|
|
|
- refreshChart={refreshChart}
|
|
|
+ chart={currentChart}
|
|
|
+ refreshChart={() => getChartData(currentChart)}
|
|
|
setRevision={setRevision}
|
|
|
forceRefreshRevisions={forceRefreshRevisions}
|
|
|
refreshRevisionsOff={() => setForceRefreshRevisions(false)}
|
|
|
- status={status}
|
|
|
+ status={chartStatus}
|
|
|
shouldUpdate={
|
|
|
- chart.latest_version &&
|
|
|
- chart.latest_version !== chart.chart.metadata.version
|
|
|
+ currentChart.latest_version &&
|
|
|
+ currentChart.latest_version !==
|
|
|
+ currentChart.chart.metadata.version
|
|
|
}
|
|
|
- latestVersion={chart.latest_version}
|
|
|
+ latestVersion={currentChart.latest_version}
|
|
|
upgradeVersion={handleUpgradeVersion}
|
|
|
/>
|
|
|
</HeaderWrapper>
|
|
|
@@ -759,9 +683,9 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
<FormWrapper
|
|
|
isReadOnly={
|
|
|
imageIsPlaceholder ||
|
|
|
- props.isAuthorized("application", "", ["get", "update"])
|
|
|
+ !isAuthorized("application", "", ["get", "update"])
|
|
|
}
|
|
|
- formData={formData}
|
|
|
+ formData={currentChart.form}
|
|
|
tabOptions={tabOptions}
|
|
|
isInModal={true}
|
|
|
renderTabContents={renderTabContents}
|
|
|
@@ -784,7 +708,7 @@ const ExpandedChart: React.FC<PropsType> = (props) => {
|
|
|
);
|
|
|
};
|
|
|
|
|
|
-export default withAuth(ExpandedChart);
|
|
|
+export default ExpandedChart;
|
|
|
|
|
|
const TextWrap = styled.div``;
|
|
|
|