|
@@ -11,7 +11,7 @@ import { Context } from "shared/Context";
|
|
|
import { ChartType } from "shared/types";
|
|
import { ChartType } from "shared/types";
|
|
|
import ResourceTab from "components/ResourceTab";
|
|
import ResourceTab from "components/ResourceTab";
|
|
|
import ConfirmOverlay from "components/ConfirmOverlay";
|
|
import ConfirmOverlay from "components/ConfirmOverlay";
|
|
|
-import { useWebsockets } from "shared/hooks/useWebsockets";
|
|
|
|
|
|
|
+import { NewWebsocketOptions, useWebsockets } from "shared/hooks/useWebsockets";
|
|
|
import PodRow from "./PodRow";
|
|
import PodRow from "./PodRow";
|
|
|
|
|
|
|
|
type PropsType = {
|
|
type PropsType = {
|
|
@@ -399,6 +399,7 @@ const ControllerTabFC: React.FunctionComponent<PropsType> = ({
|
|
|
selectedPod,
|
|
selectedPod,
|
|
|
}) => {
|
|
}) => {
|
|
|
const [pods, setPods] = useState<ControllerTabPodType[]>([]);
|
|
const [pods, setPods] = useState<ControllerTabPodType[]>([]);
|
|
|
|
|
+ const [rawPodList, setRawPodList] = useState<any[]>([]);
|
|
|
const [podPendingDelete, setPodPendingDelete] = useState<any>(null);
|
|
const [podPendingDelete, setPodPendingDelete] = useState<any>(null);
|
|
|
const [available, setAvailable] = useState<number>(null);
|
|
const [available, setAvailable] = useState<number>(null);
|
|
|
const [total, setTotal] = useState<number>(null);
|
|
const [total, setTotal] = useState<number>(null);
|
|
@@ -407,7 +408,12 @@ const ControllerTabFC: React.FunctionComponent<PropsType> = ({
|
|
|
const { currentCluster, currentProject, setCurrentError } = useContext(
|
|
const { currentCluster, currentProject, setCurrentError } = useContext(
|
|
|
Context
|
|
Context
|
|
|
);
|
|
);
|
|
|
- const {} = useWebsockets();
|
|
|
|
|
|
|
+ const {
|
|
|
|
|
+ newWebsocket,
|
|
|
|
|
+ openWebsocket,
|
|
|
|
|
+ closeAllWebsockets,
|
|
|
|
|
+ closeWebsocket,
|
|
|
|
|
+ } = useWebsockets();
|
|
|
|
|
|
|
|
const currentSelectors = useMemo(() => {
|
|
const currentSelectors = useMemo(() => {
|
|
|
if (controller.kind.toLowerCase() == "job" && selectors) {
|
|
if (controller.kind.toLowerCase() == "job" && selectors) {
|
|
@@ -459,18 +465,45 @@ const ControllerTabFC: React.FunctionComponent<PropsType> = ({
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
setPods(newPods);
|
|
setPods(newPods);
|
|
|
|
|
+ setRawPodList(data);
|
|
|
if (!userSelectedPod) {
|
|
if (!userSelectedPod) {
|
|
|
let status = getPodStatus(newPods[0].status);
|
|
let status = getPodStatus(newPods[0].status);
|
|
|
status === "failed" &&
|
|
status === "failed" &&
|
|
|
newPods[0].status?.message &&
|
|
newPods[0].status?.message &&
|
|
|
setPodError(newPods[0].status?.message);
|
|
setPodError(newPods[0].status?.message);
|
|
|
- selectPod(newPods[0]);
|
|
|
|
|
|
|
+ handleSelectPod(newPods[0], data);
|
|
|
}
|
|
}
|
|
|
} catch (error) {}
|
|
} catch (error) {}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ const handleSelectPod = (pod: ControllerTabPodType, rawList?: any[]) => {
|
|
|
|
|
+ console.log(rawPodList);
|
|
|
|
|
+ const rawPod = [...rawPodList, ...(rawList || [])].find(
|
|
|
|
|
+ (rawPod) => rawPod?.metadata?.name === pod?.name
|
|
|
|
|
+ );
|
|
|
|
|
+ selectPod(rawPod);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const currentSelectedPod = useMemo(() => {
|
|
|
|
|
+ const pod = selectedPod;
|
|
|
|
|
+ const replicaSetName =
|
|
|
|
|
+ Array.isArray(pod?.metadata?.ownerReferences) &&
|
|
|
|
|
+ pod?.metadata?.ownerReferences[0]?.name;
|
|
|
|
|
+ return {
|
|
|
|
|
+ namespace: pod?.metadata?.namespace,
|
|
|
|
|
+ name: pod?.metadata?.name,
|
|
|
|
|
+ phase: pod?.status?.phase,
|
|
|
|
|
+ status: pod?.status,
|
|
|
|
|
+ replicaSetName,
|
|
|
|
|
+ } as ControllerTabPodType;
|
|
|
|
|
+ }, [selectedPod]);
|
|
|
|
|
+
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
updatePods();
|
|
updatePods();
|
|
|
|
|
+ [controller?.kind, "pod"].forEach((kind) => {
|
|
|
|
|
+ setupWebsocket(kind, controller?.metadata?.uid);
|
|
|
|
|
+ });
|
|
|
|
|
+ () => closeAllWebsockets();
|
|
|
}, [currentSelectors, controller, currentCluster, currentProject]);
|
|
}, [currentSelectors, controller, currentCluster, currentProject]);
|
|
|
|
|
|
|
|
const currentControllerStatus = useMemo(() => {
|
|
const currentControllerStatus = useMemo(() => {
|
|
@@ -567,6 +600,69 @@ const ControllerTabFC: React.FunctionComponent<PropsType> = ({
|
|
|
}
|
|
}
|
|
|
}, [pods]);
|
|
}, [pods]);
|
|
|
|
|
|
|
|
|
|
+ const getAvailability = (kind: string, c: any) => {
|
|
|
|
|
+ switch (kind?.toLowerCase()) {
|
|
|
|
|
+ case "deployment":
|
|
|
|
|
+ case "replicaset":
|
|
|
|
|
+ return [
|
|
|
|
|
+ c.status?.availableReplicas ||
|
|
|
|
|
+ c.status?.replicas - c.status?.unavailableReplicas ||
|
|
|
|
|
+ 0,
|
|
|
|
|
+ c.status?.replicas || 0,
|
|
|
|
|
+ ];
|
|
|
|
|
+ case "statefulset":
|
|
|
|
|
+ return [c.status?.readyReplicas || 0, c.status?.replicas || 0];
|
|
|
|
|
+ case "daemonset":
|
|
|
|
|
+ return [
|
|
|
|
|
+ c.status?.numberAvailable || 0,
|
|
|
|
|
+ c.status?.desiredNumberScheduled || 0,
|
|
|
|
|
+ ];
|
|
|
|
|
+ case "job":
|
|
|
|
|
+ return [1, 1];
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const setupWebsocket = (kind: string, controllerUid: string) => {
|
|
|
|
|
+ let apiEndpoint = `/api/projects/${currentProject.id}/k8s/${kind}/status?cluster_id=${currentCluster.id}`;
|
|
|
|
|
+ if (kind == "pod" && currentSelectors) {
|
|
|
|
|
+ apiEndpoint += `&selectors=${currentSelectors[0]}`;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const options: NewWebsocketOptions = {};
|
|
|
|
|
+ options.onopen = () => {
|
|
|
|
|
+ console.log("connected to websocket");
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ options.onmessage = (evt: MessageEvent) => {
|
|
|
|
|
+ let event = JSON.parse(evt.data);
|
|
|
|
|
+ let object = event.Object;
|
|
|
|
|
+ object.metadata.kind = event.Kind;
|
|
|
|
|
+
|
|
|
|
|
+ // update pods no matter what if ws message is a pod event.
|
|
|
|
|
+ // If controller event, check if ws message corresponds to the designated controller in props.
|
|
|
|
|
+ if (event.Kind != "pod" && object.metadata.uid !== controllerUid) return;
|
|
|
|
|
+
|
|
|
|
|
+ if (event.Kind != "pod") {
|
|
|
|
|
+ let [available, total] = getAvailability(object.metadata.kind, object);
|
|
|
|
|
+ setAvailable(available);
|
|
|
|
|
+ setTotal(total);
|
|
|
|
|
+ }
|
|
|
|
|
+ updatePods();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ options.onclose = () => {
|
|
|
|
|
+ console.log("closing websocket");
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ options.onerror = (err: ErrorEvent) => {
|
|
|
|
|
+ console.log(err);
|
|
|
|
|
+ closeWebsocket(kind);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ newWebsocket(kind, apiEndpoint, options);
|
|
|
|
|
+ openWebsocket(kind);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
return (
|
|
return (
|
|
|
<ResourceTab
|
|
<ResourceTab
|
|
|
label={controller.kind}
|
|
label={controller.kind}
|
|
@@ -582,7 +678,7 @@ const ControllerTabFC: React.FunctionComponent<PropsType> = ({
|
|
|
<PodRow
|
|
<PodRow
|
|
|
key={i}
|
|
key={i}
|
|
|
pod={pod}
|
|
pod={pod}
|
|
|
- isSelected={selectedPod?.name === pod?.name}
|
|
|
|
|
|
|
+ isSelected={currentSelectedPod?.name === pod?.name}
|
|
|
podStatus={status}
|
|
podStatus={status}
|
|
|
isLastItem={i === pods.length - 1}
|
|
isLastItem={i === pods.length - 1}
|
|
|
onTabClick={() => {
|
|
onTabClick={() => {
|
|
@@ -590,7 +686,7 @@ const ControllerTabFC: React.FunctionComponent<PropsType> = ({
|
|
|
status === "failed" &&
|
|
status === "failed" &&
|
|
|
pod.status?.message &&
|
|
pod.status?.message &&
|
|
|
setPodError(pod.status?.message);
|
|
setPodError(pod.status?.message);
|
|
|
- selectPod(pod);
|
|
|
|
|
|
|
+ handleSelectPod(pod);
|
|
|
setUserSelectedPod(true);
|
|
setUserSelectedPod(true);
|
|
|
}}
|
|
}}
|
|
|
onDeleteClick={() => setPodPendingDelete(pod)}
|
|
onDeleteClick={() => setPodPendingDelete(pod)}
|