-
-
-
- Cluster Nodes
-
-
+
+
+ Cluster Nodes
+
+
+
This section lists the nodes that are configured and shows the
- status/health of each. To add additional nodes to this cluster,
- click the "Add node" button at the bottom of this page.
+ status/health of each.
-
- {(nodes?.nodes || testData?.nodes) &&
- (nodes?.nodes || testData?.nodes).map((node, i) => (
-
- ))}
-
-
- {Utilities.sessionRolesHasOneOf([rbacRoles.CLUSTER_ADMIN]) && (
-
-
- Add a node
+ {Utilities.sessionRolesHasOneOf([rbacRoles.CLUSTER_ADMIN]) && (
+
+ Add node type
-
+ )}
+
+
+ {(nodes?.nodes || testData?.nodes) &&
+ (nodes?.nodes || testData?.nodes).map((node, i) => (
+
+ ))}
+
+ {fromLicenseFlow && (
+
+ Continue
+
)}
+ {/* MODALS */}
setState({ displayAddNode: false })}
@@ -420,7 +424,7 @@ const HelmVMClusterManagement = () => {
- Add A Node
+ Add a Node Type
{
/>
- To add a node to this cluster, select the type of node you are
+ To add a node type to this cluster, select the type of node you are
adding, and then select an installation method below. This screen
will automatically show the status when the node successfully joins
the cluster.
@@ -468,11 +472,12 @@ const HelmVMClusterManagement = () => {
Copied!}
>
- {`curl http://node.something/join?token=abc&labels=${selectedNodeTypes.join(
+ {`curl ${generatePrimaryAddNodeCommand}?token=abc&labels=${selectedNodeTypes.join(
","
)}`}
@@ -504,7 +509,7 @@ const HelmVMClusterManagement = () => {
disabled={selectedNodeTypes.length === 0}
onClick={() => setState({ displayAddNode: false })}
>
- Add node
+ Add node type
diff --git a/web/src/components/apps/HelmVMViewNode.jsx b/web/src/components/apps/HelmVMViewNode.jsx
index c607a3ed08..f47d18c4f9 100644
--- a/web/src/components/apps/HelmVMViewNode.jsx
+++ b/web/src/components/apps/HelmVMViewNode.jsx
@@ -1,5 +1,7 @@
+import { MaterialReactTable } from "material-react-table";
import React, { useMemo } from "react";
import { useQuery } from "react-query";
+import { useParams } from "react-router";
import { Link } from "react-router-dom";
const testData = {
@@ -41,6 +43,196 @@ const testData = {
pidPressure: false,
ready: true,
},
+ podList: [
+ {
+ metadata: {
+ name: "example-es-85fc9df74-g9jbn",
+ generateName: "example-es-85fc9df74-",
+ namespace: "helmvm",
+ uid: "1caba3fb-bd52-430a-9cff-0eb0939317fa",
+ resourceVersion: "40284",
+ creationTimestamp: "2023-10-17T16:22:37Z",
+ labels: {
+ app: "example",
+ component: "es",
+ "kots.io/app-slug": "laverya-minimal-kots",
+ "kots.io/backup": "velero",
+ "pod-template-hash": "85fc9df74",
+ },
+ annotations: {
+ "cni.projectcalico.org/containerID":
+ "c3fa12aad2ed6f726ecda31f7f94d1224c9f50a805a9efc67aaf4959e464434c",
+ "cni.projectcalico.org/podIP": "10.244.45.141/32",
+ "cni.projectcalico.org/podIPs": "10.244.45.141/32",
+ "kots.io/app-slug": "laverya-minimal-kots",
+ },
+ ownerReferences: [
+ {
+ apiVersion: "apps/v1",
+ kind: "ReplicaSet",
+ name: "example-es-85fc9df74",
+ uid: "b5008bca-1ad0-4107-8603-397fc3be74f8",
+ controller: true,
+ blockOwnerDeletion: true,
+ },
+ ],
+ },
+ spec: {
+ volumes: [
+ {
+ name: "kube-api-access-fhfc4",
+ projected: {
+ sources: [
+ {
+ serviceAccountToken: {
+ expirationSeconds: 3607,
+ path: "token",
+ },
+ },
+ {
+ configMap: {
+ name: "kube-root-ca.crt",
+ items: [{ key: "ca.crt", path: "ca.crt" }],
+ },
+ },
+ {
+ downwardAPI: {
+ items: [
+ {
+ path: "namespace",
+ fieldRef: {
+ apiVersion: "v1",
+ fieldPath: "metadata.namespace",
+ },
+ },
+ ],
+ },
+ },
+ ],
+ defaultMode: 420,
+ },
+ },
+ ],
+ containers: [
+ {
+ name: "es",
+ image:
+ "docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.21",
+ envFrom: [{ configMapRef: { name: "example-config" } }],
+ resources: {
+ limits: { cpu: "500m", memory: "256Mi" },
+ requests: { cpu: "50m", memory: "16Mi" },
+ },
+ volumeMounts: [
+ {
+ name: "kube-api-access-fhfc4",
+ readOnly: true,
+ mountPath: "/var/run/secrets/kubernetes.io/serviceaccount",
+ },
+ ],
+ terminationMessagePath: "/dev/termination-log",
+ terminationMessagePolicy: "File",
+ imagePullPolicy: "IfNotPresent",
+ },
+ ],
+ restartPolicy: "Always",
+ terminationGracePeriodSeconds: 30,
+ dnsPolicy: "ClusterFirst",
+ serviceAccountName: "default",
+ serviceAccount: "default",
+ nodeName: "laverya-helmvm",
+ securityContext: {},
+ imagePullSecrets: [{ name: "laverya-minimal-kots-registry" }],
+ schedulerName: "default-scheduler",
+ tolerations: [
+ {
+ key: "node.kubernetes.io/not-ready",
+ operator: "Exists",
+ effect: "NoExecute",
+ tolerationSeconds: 300,
+ },
+ {
+ key: "node.kubernetes.io/unreachable",
+ operator: "Exists",
+ effect: "NoExecute",
+ tolerationSeconds: 300,
+ },
+ ],
+ priority: 0,
+ enableServiceLinks: true,
+ preemptionPolicy: "PreemptLowerPriority",
+ },
+ status: {
+ phase: "Running",
+ conditions: [
+ {
+ type: "Initialized",
+ status: "True",
+ lastProbeTime: null,
+ lastTransitionTime: "2023-10-17T16:22:37Z",
+ },
+ {
+ type: "Ready",
+ status: "False",
+ lastProbeTime: null,
+ lastTransitionTime: "2023-10-17T19:55:16Z",
+ reason: "ContainersNotReady",
+ message: "containers with unready status: [es]",
+ },
+ {
+ type: "ContainersReady",
+ status: "False",
+ lastProbeTime: null,
+ lastTransitionTime: "2023-10-17T19:55:16Z",
+ reason: "ContainersNotReady",
+ message: "containers with unready status: [es]",
+ },
+ {
+ type: "PodScheduled",
+ status: "True",
+ lastProbeTime: null,
+ lastTransitionTime: "2023-10-17T16:22:37Z",
+ },
+ ],
+ hostIP: "10.128.0.44",
+ podIP: "10.244.45.141",
+ podIPs: [{ ip: "10.244.45.141" }],
+ startTime: "2023-10-17T16:22:37Z",
+ containerStatuses: [
+ {
+ name: "es",
+ state: {
+ waiting: {
+ reason: "CrashLoopBackOff",
+ message:
+ "back-off 5m0s restarting failed container=es pod=example-es-85fc9df74-g9jbn_helmvm(1caba3fb-bd52-430a-9cff-0eb0939317fa)",
+ },
+ },
+ lastState: {
+ terminated: {
+ exitCode: 137,
+ reason: "OOMKilled",
+ startedAt: "2023-10-17T19:55:11Z",
+ finishedAt: "2023-10-17T19:55:13Z",
+ containerID:
+ "containerd://9cce5c792b7ad61d040f7b8aca042d13a714100c75ebc40e71eb5444bbb65e83",
+ },
+ },
+ ready: false,
+ restartCount: 46,
+ image:
+ "docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.21",
+ imageID:
+ "docker.elastic.co/elasticsearch/elasticsearch-oss@sha256:86e7750c4d896d41bd638b6e510e0610b98fd9fa48f8caeeed8ccd8424b1dc9f",
+ containerID:
+ "containerd://9cce5c792b7ad61d040f7b8aca042d13a714100c75ebc40e71eb5444bbb65e83",
+ started: false,
+ },
+ ],
+ qosClass: "Burstable",
+ },
+ },
+ ],
},
{
name: "test-helmvm-worker",
@@ -79,11 +271,13 @@ const testData = {
};
const HelmVMViewNode = () => {
- // const { data: nodes } = useQuery({
- // queryKey: "helmVmNodes",
- // queryFn: async () => {
+ // const { nodeName } = useParams();
+ // const { data: node } = useQuery({
+ // queryKey: ["helmVmNode", nodeName],
+ // queryFn: async ({ queryKey }) => {
+ // const [, nodeName] = queryKey;
// return (
- // await fetch(`${process.env.API_ENDPOINT}/helmvm/nodes`, {
+ // await fetch(`${process.env.API_ENDPOINT}/helmvm/node/${nodeName}`, {
// headers: {
// Accept: "application/json",
// },
@@ -125,14 +319,14 @@ const HelmVMViewNode = () => {
size: 150,
},
{
- accessorKey: "isConnected",
- header: "Connection",
+ accessorKey: "status",
+ header: "Status",
size: 150,
},
{
- accessorKey: "kubeletVersion",
- header: "Kubelet Version",
- size: 170,
+ accessorKey: "disk",
+ header: "Disk",
+ size: 150,
},
{
accessorKey: "cpu",
@@ -144,20 +338,30 @@ const HelmVMViewNode = () => {
header: "Memory",
size: 150,
},
- {
- accessorKey: "pods",
- header: "Pods",
- size: 150,
- },
{
accessorKey: "canDelete",
- header: "Delete Node",
+ header: "Delete Pod",
size: 150,
},
],
[]
);
+ const mappedPods = useMemo(() => {
+ return node.podList.map((n) => ({
+ name: n.metadata.name,
+ status: n.status.phase,
+ disk: null,
+ cpu: null,
+ memory: null,
+ canDelete: (
+ <>
+
Delete
+ >
+ ),
+ }));
+ }, [node.podList]);
+
return (
{/* Breadcrumbs */}
@@ -189,7 +393,7 @@ const HelmVMViewNode = () => {
tw-shadow-md tw-p-3"
>
Pods
-
+ {/*
{columns.map((col) => {
@@ -204,7 +408,46 @@ const HelmVMViewNode = () => {
Some pods here
-
+
*/}
+
{/* Troubleshooting */}
Date: Tue, 17 Oct 2023 17:04:33 -0600
Subject: [PATCH 08/17] add material react table to display pods
---
web/src/components/apps/HelmVMViewNode.jsx | 20 ++------------------
1 file changed, 2 insertions(+), 18 deletions(-)
diff --git a/web/src/components/apps/HelmVMViewNode.jsx b/web/src/components/apps/HelmVMViewNode.jsx
index f47d18c4f9..7992ca57be 100644
--- a/web/src/components/apps/HelmVMViewNode.jsx
+++ b/web/src/components/apps/HelmVMViewNode.jsx
@@ -363,7 +363,7 @@ const HelmVMViewNode = () => {
}, [node.podList]);
return (
-
+
{/* Breadcrumbs */}
{
tw-shadow-md tw-p-3"
>
Pods
- {/*
-
-
- {columns.map((col) => {
- return (
-
-
- {col.header}
-
-
- );
- })}
-
-
- Some pods here
-
*/}
{
muiTablePaperProps={{
sx: {
width: "100%",
- marginTop: "28px",
+ boxShadow: "none",
},
}}
initialState={{ density: "compact" }}
From 75d35971a20eaa174c240c29b5416587ebd16c00 Mon Sep 17 00:00:00 2001
From: Star Richardson <67430892+alicenstar@users.noreply.github.com>
Date: Tue, 17 Oct 2023 17:11:12 -0600
Subject: [PATCH 09/17] revert change
---
web/dist/README.md | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 web/dist/README.md
diff --git a/web/dist/README.md b/web/dist/README.md
new file mode 100644
index 0000000000..7f1a1f8cb0
--- /dev/null
+++ b/web/dist/README.md
@@ -0,0 +1,3 @@
+The dist directory is used in the go build to embed the compiled web resources into the kotsadm binary. Because
+web isn't always built (testing, okteto, etc), this README.md will allow compiling of the go binary without first
+building web.
\ No newline at end of file
From a1cae3b6a20744b154c5e033d987488f73c6008d Mon Sep 17 00:00:00 2001
From: Star Richardson <67430892+alicenstar@users.noreply.github.com>
Date: Tue, 17 Oct 2023 17:25:05 -0600
Subject: [PATCH 10/17] uncomment real queries
---
.../apps/HelmVMClusterManagement.jsx | 77 ++++++++-----------
web/src/components/apps/HelmVMViewNode.jsx | 72 ++++++++---------
2 files changed, 70 insertions(+), 79 deletions(-)
diff --git a/web/src/components/apps/HelmVMClusterManagement.jsx b/web/src/components/apps/HelmVMClusterManagement.jsx
index 189560449d..4e2ede85d0 100644
--- a/web/src/components/apps/HelmVMClusterManagement.jsx
+++ b/web/src/components/apps/HelmVMClusterManagement.jsx
@@ -113,45 +113,42 @@ const HelmVMClusterManagement = ({ fromLicenseFlow = false }) => {
const navigate = useNavigate();
- const nodes = testData;
- const nodesLoading = false;
-
// #region queries
- // const { data: nodes, isLoading: nodesLoading } = useQuery({
- // queryKey: "helmVmNodes",
- // queryFn: async () => {
- // return (
- // await fetch(`${process.env.API_ENDPOINT}/helmvm/nodes`, {
- // headers: {
- // Accept: "application/json",
- // },
- // credentials: "include",
- // method: "GET",
- // })
- // ).json();
- // },
- // onError: (err) => {
- // if (err.status === 401) {
- // Utilities.logoutUser();
- // return;
- // }
- // console.log(
- // "failed to get node status list, unexpected status code",
- // err.status
- // );
- // },
- // onSuccess: (data) => {
- // setState({
- // // if cluster doesn't support ha, then primary will be disabled. Force into secondary
- // selectedNodeType: !data.ha ? "secondary" : state.selectedNodeType,
- // });
- // },
- // config: {
- // refetchInterval: 1000,
- // retry: false,
- // },
- // });
+ const { data: nodes, isLoading: nodesLoading } = useQuery({
+ queryKey: "helmVmNodes",
+ queryFn: async () => {
+ return (
+ await fetch(`${process.env.API_ENDPOINT}/helmvm/nodes`, {
+ headers: {
+ Accept: "application/json",
+ },
+ credentials: "include",
+ method: "GET",
+ })
+ ).json();
+ },
+ onError: (err) => {
+ if (err.status === 401) {
+ Utilities.logoutUser();
+ return;
+ }
+ console.log(
+ "failed to get node status list, unexpected status code",
+ err.status
+ );
+ },
+ onSuccess: (data) => {
+ setState({
+ // if cluster doesn't support ha, then primary will be disabled. Force into secondary
+ selectedNodeType: !data.ha ? "secondary" : state.selectedNodeType,
+ });
+ },
+ config: {
+ refetchInterval: 1000,
+ retry: false,
+ },
+ });
const {
data: generateSecondaryAddNodeCommand,
@@ -216,12 +213,6 @@ const HelmVMClusterManagement = ({ fromLicenseFlow = false }) => {
})
).json();
},
- onSuccess: () => {
- // if (fromLicenseFlow && data.isConfigurable) {
- // navigate(`/${data.slug}/config`, { replace: true });
- // return;
- // }
- },
});
// #endregion
diff --git a/web/src/components/apps/HelmVMViewNode.jsx b/web/src/components/apps/HelmVMViewNode.jsx
index 7992ca57be..e22874c3ca 100644
--- a/web/src/components/apps/HelmVMViewNode.jsx
+++ b/web/src/components/apps/HelmVMViewNode.jsx
@@ -271,43 +271,43 @@ const testData = {
};
const HelmVMViewNode = () => {
- // const { nodeName } = useParams();
- // const { data: node } = useQuery({
- // queryKey: ["helmVmNode", nodeName],
- // queryFn: async ({ queryKey }) => {
- // const [, nodeName] = queryKey;
- // return (
- // await fetch(`${process.env.API_ENDPOINT}/helmvm/node/${nodeName}`, {
- // headers: {
- // Accept: "application/json",
- // },
- // credentials: "include",
- // method: "GET",
- // })
- // ).json();
- // },
- // onError: (err) => {
- // if (err.status === 401) {
- // Utilities.logoutUser();
- // return;
- // }
- // console.log(
- // "failed to get node status list, unexpected status code",
- // err.status
- // );
- // },
- // onSuccess: (data) => {
- // setState({
- // // if cluster doesn't support ha, then primary will be disabled. Force into secondary
- // selectedNodeType: !data.ha ? "secondary" : state.selectedNodeType,
- // });
- // },
- // config: {
- // retry: false,
- // },
- // });
+ const { nodeName } = useParams();
+ const { data: nodeData } = useQuery({
+ queryKey: ["helmVmNode", nodeName],
+ queryFn: async ({ queryKey }) => {
+ const [, nodeName] = queryKey;
+ return (
+ await fetch(`${process.env.API_ENDPOINT}/helmvm/node/${nodeName}`, {
+ headers: {
+ Accept: "application/json",
+ },
+ credentials: "include",
+ method: "GET",
+ })
+ ).json();
+ },
+ onError: (err) => {
+ if (err.status === 401) {
+ Utilities.logoutUser();
+ return;
+ }
+ console.log(
+ "failed to get node status list, unexpected status code",
+ err.status
+ );
+ },
+ onSuccess: (data) => {
+ setState({
+ // if cluster doesn't support ha, then primary will be disabled. Force into secondary
+ selectedNodeType: !data.ha ? "secondary" : state.selectedNodeType,
+ });
+ },
+ config: {
+ retry: false,
+ },
+ });
- const node = testData.nodes[0];
+ const node = nodeData || testData.nodes[0];
const columns = useMemo(
() => [
From 8240eaa8c7542cecf27ec706f2689ddbfb323aad Mon Sep 17 00:00:00 2001
From: Star Richardson <67430892+alicenstar@users.noreply.github.com>
Date: Wed, 18 Oct 2023 10:19:08 -0600
Subject: [PATCH 11/17] fix useparams import
---
web/src/components/apps/HelmVMViewNode.jsx | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/web/src/components/apps/HelmVMViewNode.jsx b/web/src/components/apps/HelmVMViewNode.jsx
index e22874c3ca..c3337352d0 100644
--- a/web/src/components/apps/HelmVMViewNode.jsx
+++ b/web/src/components/apps/HelmVMViewNode.jsx
@@ -1,8 +1,7 @@
import { MaterialReactTable } from "material-react-table";
import React, { useMemo } from "react";
import { useQuery } from "react-query";
-import { useParams } from "react-router";
-import { Link } from "react-router-dom";
+import { Link, useParams } from "react-router-dom";
const testData = {
isHelmVMEnabled: true,
From 300856e4d789c58892ec59789f3aa9d4d116c8b1 Mon Sep 17 00:00:00 2001
From: Star Richardson <67430892+alicenstar@users.noreply.github.com>
Date: Wed, 18 Oct 2023 11:51:43 -0600
Subject: [PATCH 12/17] fix params/routing, update api route
---
web/src/Root.tsx | 25 +-
...gement.jsx => HelmVMClusterManagement.tsx} | 288 ++++++++++--------
web/src/components/apps/HelmVMNodeRow.jsx | 24 +-
web/src/components/apps/HelmVMViewNode.jsx | 4 +-
4 files changed, 188 insertions(+), 153 deletions(-)
rename web/src/components/apps/{HelmVMClusterManagement.jsx => HelmVMClusterManagement.tsx} (73%)
diff --git a/web/src/Root.tsx b/web/src/Root.tsx
index e9c942f384..f04f6951fd 100644
--- a/web/src/Root.tsx
+++ b/web/src/Root.tsx
@@ -538,8 +538,8 @@ const Root = () => {
appSlugFromMetadata={state.appSlugFromMetadata || ""}
fetchingMetadata={state.fetchingMetadata}
onUploadSuccess={getAppsList}
- isKurl={state.isKurl}
- isHelmVM={state.isHelmVM}
+ isKurl={!!state.isKurl}
+ isHelmVM={!!state.isHelmVM}
/>
}
/>
@@ -585,7 +585,12 @@ const Root = () => {
{/* {state.adminConsoleMetadata?.isHelmVM && ( */}
}
+ element={
+
+ }
/>
{/* )} */}
{/* {(state.adminConsoleMetadata?.isKurl ||
@@ -594,7 +599,7 @@ const Root = () => {
path="/:slug/cluster/manage"
element={
state.adminConsoleMetadata?.isKurl ? (
-
+
) : (
)
@@ -602,7 +607,10 @@ const Root = () => {
/>
{/* )} */}
{/* {state.adminConsoleMetadata?.isHelmVM && ( */}
- } />
+ }
+ />
{/* )} */}
{
} />
- }
+ element={ }
/>
{/* WHERE IS SELECTEDAPP */}
{state.app?.isAppIdentityServiceSupported && (
diff --git a/web/src/components/apps/HelmVMClusterManagement.jsx b/web/src/components/apps/HelmVMClusterManagement.tsx
similarity index 73%
rename from web/src/components/apps/HelmVMClusterManagement.jsx
rename to web/src/components/apps/HelmVMClusterManagement.tsx
index 4e2ede85d0..a51ecaddc9 100644
--- a/web/src/components/apps/HelmVMClusterManagement.jsx
+++ b/web/src/components/apps/HelmVMClusterManagement.tsx
@@ -1,13 +1,12 @@
import classNames from "classnames";
-import dayjs from "dayjs";
-import React, { useEffect, useMemo, useReducer, useState } from "react";
+import React, { ChangeEvent, useReducer, useState } from "react";
import Modal from "react-modal";
-import { useMutation, useQuery } from "react-query";
+import { useQuery } from "react-query";
import { useNavigate } from "react-router-dom";
import { KotsPageTitle } from "@components/Head";
+import { useApps } from "@features/App";
import { rbacRoles } from "../../constants/rbac";
-import { Repeater } from "../../utilities/repeater";
import { Utilities } from "../../utilities/utilities";
import Icon from "../Icon";
import ErrorModal from "../modals/ErrorModal";
@@ -93,50 +92,106 @@ const testData = {
],
};
-const HelmVMClusterManagement = ({ fromLicenseFlow = false }) => {
+type State = {
+ displayAddNode: boolean;
+ selectedNodeType: string;
+ confirmDeleteNode: string;
+ deleteNodeError: string;
+ showConfirmDrainModal: boolean;
+ nodeNameToDrain: string;
+ drainingNodeName: string | null;
+ drainNodeSuccessful: boolean;
+};
+
+const HelmVMClusterManagement = ({
+ fromLicenseFlow = false,
+ appName,
+}: {
+ fromLicenseFlow?: boolean;
+ appName?: string;
+}) => {
const [state, setState] = useReducer(
- (state, newState) => ({ ...state, ...newState }),
+ (prevState: State, newState: Partial) => ({
+ ...prevState,
+ ...newState,
+ }),
{
displayAddNode: false,
selectedNodeType: "primary",
- helmvm: null,
- deletNodeError: "",
confirmDeleteNode: "",
+ deleteNodeError: "",
showConfirmDrainModal: false,
nodeNameToDrain: "",
drainingNodeName: null,
drainNodeSuccessful: false,
}
);
- const [selectedNodeTypes, setSelectedNodeTypes] = useState([]);
+ const [selectedNodeTypes, setSelectedNodeTypes] = useState([]);
const [useStaticToken, setUseStaticToken] = useState(false);
const navigate = useNavigate();
// #region queries
- const { data: nodes, isLoading: nodesLoading } = useQuery({
+ type NodesResponse = {
+ ha: boolean;
+ isHelmVMEnabled: boolean;
+ nodes: {
+ name: string;
+ isConnected: boolean;
+ isReady: boolean;
+ isPrimaryNode: boolean;
+ canDelete: boolean;
+ kubeletVersion: string;
+ cpu: {
+ capacity: number;
+ available: number;
+ };
+ memory: {
+ capacity: number;
+ available: number;
+ };
+ pods: {
+ capacity: number;
+ available: number;
+ };
+ labels: string[];
+ conditions: {
+ memoryPressure: boolean;
+ diskPressure: boolean;
+ pidPressure: boolean;
+ ready: boolean;
+ };
+ }[];
+ };
+
+ const { data: nodesData, isLoading: nodesLoading } = useQuery<
+ NodesResponse,
+ Error,
+ NodesResponse,
+ string
+ >({
queryKey: "helmVmNodes",
queryFn: async () => {
- return (
- await fetch(`${process.env.API_ENDPOINT}/helmvm/nodes`, {
- headers: {
- Accept: "application/json",
- },
- credentials: "include",
- method: "GET",
- })
- ).json();
- },
- onError: (err) => {
- if (err.status === 401) {
+ const res = await fetch(`${process.env.API_ENDPOINT}/helmvm/nodes`, {
+ headers: {
+ Accept: "application/json",
+ },
+ credentials: "include",
+ method: "GET",
+ });
+ if (res.status === 401) {
Utilities.logoutUser();
- return;
+ console.log(
+ "failed to get node status list, unexpected status code",
+ res.status
+ );
+ const error = await res.json();
+ throw new Error(
+ error?.error?.message || error?.error || error?.message
+ );
}
- console.log(
- "failed to get node status list, unexpected status code",
- err.status
- );
+ return res.json();
},
onSuccess: (data) => {
setState({
@@ -144,45 +199,20 @@ const HelmVMClusterManagement = ({ fromLicenseFlow = false }) => {
selectedNodeType: !data.ha ? "secondary" : state.selectedNodeType,
});
},
- config: {
- refetchInterval: 1000,
- retry: false,
- },
- });
-
- const {
- data: generateSecondaryAddNodeCommand,
- isLoading: generateSecondaryAddNodeCommandLoading,
- error: generateSecondaryAddNodeCommandError,
- } = useQuery({
- queryKey: "generateSecondaryAddNodeCommand",
- queryFn: async () => {
- return (
- await fetch(
- `${process.env.API_ENDPOINT}/helmvm/generate-node-join-command-secondary`,
- {
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
- credentials: "include",
- method: "POST",
- }
- )
- ).json();
- },
+ refetchInterval: 1000,
+ retry: false,
});
const {
- data: generatePrimaryAddNodeCommand,
- isLoading: generatePrimaryAddNodeCommandLoading,
- error: generatePrimaryAddNodeCommandError,
+ data: generateAddNodeCommand,
+ isLoading: generateAddNodeCommandLoading,
+ // error: generateAddNodeCommandError,
} = useQuery({
- queryKey: "generatePrimaryAddNodeCommand",
+ queryKey: "generateAddNodeCommand",
queryFn: async () => {
return (
await fetch(
- `${process.env.API_ENDPOINT}/helmvm/generate-node-join-command-primary`,
+ `${process.env.API_ENDPOINT}/helmvm/generate-node-join-command`,
{
headers: {
"Content-Type": "application/json",
@@ -190,33 +220,37 @@ const HelmVMClusterManagement = ({ fromLicenseFlow = false }) => {
},
credentials: "include",
method: "POST",
+ body: JSON.stringify({
+ roles: selectedNodeTypes.join(","),
+ }),
}
)
).json();
},
});
- const {
- mutate: addNodeType,
- isLoading: addNodeTypeLoading,
- error: addNodeTypeError,
- } = useMutation({
- mutationFn: async () => {
- return (
- await fetch(`${process.env.API_ENDPOINT}/helmvm/nodes`, {
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
- credentials: "include",
- method: "POST",
- })
- ).json();
- },
- });
+ // TODO: import useMutation
+ // const {
+ // mutate: addNodeType,
+ // isLoading: addNodeTypeLoading,
+ // error: addNodeTypeError,
+ // } = useMutation({
+ // mutationFn: async () => {
+ // return (
+ // await fetch(`${process.env.API_ENDPOINT}/helmvm/nodes`, {
+ // headers: {
+ // "Content-Type": "application/json",
+ // Accept: "application/json",
+ // },
+ // credentials: "include",
+ // method: "POST",
+ // })
+ // ).json();
+ // },
+ // });
// #endregion
- const deleteNode = (name) => {
+ const deleteNode = (name: string) => {
setState({
confirmDeleteNode: name,
});
@@ -256,14 +290,14 @@ const HelmVMClusterManagement = ({ fromLicenseFlow = false }) => {
});
};
- const onDrainNodeClick = (name) => {
+ const onDrainNodeClick = (name: string) => {
setState({
showConfirmDrainModal: true,
nodeNameToDrain: name,
});
};
- const drainNode = async (name) => {
+ const drainNode = async (name: string) => {
setState({ showConfirmDrainModal: false, drainingNodeName: name });
fetch(`${process.env.API_ENDPOINT}/helmvm/nodes/${name}/drain`, {
headers: {
@@ -273,7 +307,7 @@ const HelmVMClusterManagement = ({ fromLicenseFlow = false }) => {
credentials: "include",
method: "POST",
})
- .then(async (res) => {
+ .then(async () => {
setState({ drainNodeSuccessful: true });
setTimeout(() => {
setState({
@@ -301,42 +335,25 @@ const HelmVMClusterManagement = ({ fromLicenseFlow = false }) => {
setState({ deleteNodeError: "" });
};
- const NODE_TYPES = [
- "controlplane",
- "db",
- "app",
- "search",
- "webserver",
- "jobs",
- ];
+ const NODE_TYPES = ["controller"];
- const determineDisabledState = (nodeType, selectedNodeTypes) => {
- if (nodeType === "controlplane") {
- const numControlPlanes = testData.nodes.reduce((acc, node) => {
- if (node.labels.includes("controlplane")) {
- acc++;
- }
- return acc;
- });
- return numControlPlanes === 3;
- }
- if (
- (nodeType === "db" || nodeType === "search") &&
- selectedNodeTypes.includes("webserver")
- ) {
- return true;
- }
+ const determineDisabledState = () => {
+ // if (nodeType === "controller") {
+ // const numControllers = testData.nodes.reduce((acc, node) => {
+ // if (node.labels.includes("controller")) {
+ // acc += 1;
+ // }
+ // return acc;
+ // }, 0);
+ // return numControllers === 3;
+ // }
return false;
};
- const handleSelectNodeType = (e) => {
- const nodeType = e.currentTarget.value;
+ const handleSelectNodeType = (e: ChangeEvent) => {
+ let nodeType = e.currentTarget.value;
let types = selectedNodeTypes;
- if (nodeType === "webserver") {
- types = types.filter((type) => type !== "db" && type !== "search");
- }
-
if (selectedNodeTypes.includes(nodeType)) {
setSelectedNodeTypes(types.filter((type) => type !== nodeType));
} else {
@@ -344,9 +361,12 @@ const HelmVMClusterManagement = ({ fromLicenseFlow = false }) => {
}
};
+ const { data } = useApps();
+
const handleContinue = () => {
- if (data.isConfigurable) {
- navigate(`/${data.slug}/config`, { replace: true });
+ const app = data?.apps?.find((a) => a.name === appName);
+ if (app?.isConfigurable) {
+ navigate(`/${app.slug}/config`, { replace: true });
return;
}
};
@@ -382,15 +402,17 @@ const HelmVMClusterManagement = ({ fromLicenseFlow = false }) => {
)}
- {(nodes?.nodes || testData?.nodes) &&
- (nodes?.nodes || testData?.nodes).map((node, i) => (
+ {(nodesData?.nodes || testData?.nodes) &&
+ (nodesData?.nodes || testData?.nodes).map((node, i) => (
))}
@@ -436,10 +458,7 @@ const HelmVMClusterManagement = ({ fromLicenseFlow = false }) => {
key={nodeType}
className={classNames("BoxedCheckbox", {
"is-active": selectedNodeTypes.includes(nodeType),
- "is-disabled": determineDisabledState(
- nodeType,
- selectedNodeTypes
- ),
+ "is-disabled": determineDisabledState(nodeType),
})}
>
{
type="checkbox"
name={`${nodeType}NodeType`}
value={nodeType}
- disabled={determineDisabledState(nodeType, selectedNodeTypes)}
+ disabled={determineDisabledState(nodeType)}
checked={selectedNodeTypes.includes(nodeType)}
onChange={handleSelectNodeType}
/>
@@ -456,22 +475,25 @@ const HelmVMClusterManagement = ({ fromLicenseFlow = false }) => {
htmlFor={`${nodeType}NodeType`}
className="tw-block u-cursor--pointer u-userSelect--none u-textColor--primary u-fontSize--normal u-fontWeight--medium tw-text-center"
>
- {nodeType}
+ {nodeType === "controller" ? "controlplane" : nodeType}
))}
-
Copied!}
- >
- {`curl ${generatePrimaryAddNodeCommand}?token=abc&labels=${selectedNodeTypes.join(
- ","
- )}`}
-
+ {generateAddNodeCommandLoading &&
Generating command...
}
+ {!generateAddNodeCommandLoading && generateAddNodeCommand?.command && (
+
Copied!
+ }
+ >
+ {generateAddNodeCommand?.command || ""}
+
+ )}
{
if (drainNode && Utilities.sessionRolesHasOneOf(rbacRoles.DRAIN_NODE)) {
if (
@@ -60,17 +62,25 @@ export default function HelmVMNodeRow({
}
}
};
+ console.log("slug", slug);
return (
-
- {node?.name}
-
+ {slug && (
+
+ {node?.name}
+
+ )}
+ {!slug && (
+
+ {node?.name}
+
+ )}
{node?.isPrimaryNode && (
Primary node
diff --git a/web/src/components/apps/HelmVMViewNode.jsx b/web/src/components/apps/HelmVMViewNode.jsx
index c3337352d0..a72f13648f 100644
--- a/web/src/components/apps/HelmVMViewNode.jsx
+++ b/web/src/components/apps/HelmVMViewNode.jsx
@@ -270,7 +270,7 @@ const testData = {
};
const HelmVMViewNode = () => {
- const { nodeName } = useParams();
+ const { slug, nodeName } = useParams();
const { data: nodeData } = useQuery({
queryKey: ["helmVmNode", nodeName],
queryFn: async ({ queryKey }) => {
@@ -366,7 +366,7 @@ const HelmVMViewNode = () => {
{/* Breadcrumbs */}
Cluster Nodes
From 0e6db8526cb206088150b3560bbd4585038243a1 Mon Sep 17 00:00:00 2001
From: Star Richardson <67430892+alicenstar@users.noreply.github.com>
Date: Wed, 18 Oct 2023 12:05:43 -0600
Subject: [PATCH 13/17] fix loading/refetch state
---
web/src/components/apps/HelmVMClusterManagement.tsx | 2 +-
web/src/components/apps/HelmVMNodeRow.jsx | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/web/src/components/apps/HelmVMClusterManagement.tsx b/web/src/components/apps/HelmVMClusterManagement.tsx
index a51ecaddc9..e08ee32ddc 100644
--- a/web/src/components/apps/HelmVMClusterManagement.tsx
+++ b/web/src/components/apps/HelmVMClusterManagement.tsx
@@ -165,7 +165,7 @@ const HelmVMClusterManagement = ({
}[];
};
- const { data: nodesData, isLoading: nodesLoading } = useQuery<
+ const { data: nodesData, isInitialLoading: nodesLoading } = useQuery<
NodesResponse,
Error,
NodesResponse,
diff --git a/web/src/components/apps/HelmVMNodeRow.jsx b/web/src/components/apps/HelmVMNodeRow.jsx
index 94c9a42bdb..9386035869 100644
--- a/web/src/components/apps/HelmVMNodeRow.jsx
+++ b/web/src/components/apps/HelmVMNodeRow.jsx
@@ -62,7 +62,6 @@ export default function HelmVMNodeRow({
}
}
};
- console.log("slug", slug);
return (
From 766e089be0998dfc6976bc469870ed92963069af Mon Sep 17 00:00:00 2001
From: Star Richardson <67430892+alicenstar@users.noreply.github.com>
Date: Wed, 18 Oct 2023 13:07:50 -0600
Subject: [PATCH 14/17] update generate add node request
---
web/src/components/apps/HelmVMClusterManagement.tsx | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/web/src/components/apps/HelmVMClusterManagement.tsx b/web/src/components/apps/HelmVMClusterManagement.tsx
index e08ee32ddc..457f92e93f 100644
--- a/web/src/components/apps/HelmVMClusterManagement.tsx
+++ b/web/src/components/apps/HelmVMClusterManagement.tsx
@@ -208,8 +208,9 @@ const HelmVMClusterManagement = ({
isLoading: generateAddNodeCommandLoading,
// error: generateAddNodeCommandError,
} = useQuery({
- queryKey: "generateAddNodeCommand",
- queryFn: async () => {
+ queryKey: ["generateAddNodeCommand", selectedNodeTypes],
+ queryFn: async ({ queryKey }) => {
+ const [, selectedNodeTypes] = queryKey;
return (
await fetch(
`${process.env.API_ENDPOINT}/helmvm/generate-node-join-command`,
@@ -221,12 +222,13 @@ const HelmVMClusterManagement = ({
credentials: "include",
method: "POST",
body: JSON.stringify({
- roles: selectedNodeTypes.join(","),
+ roles: selectedNodeTypes,
}),
}
)
).json();
},
+ enabled: selectedNodeTypes.length > 0,
});
// TODO: import useMutation
From 3c12708661a5c838f985ddee39bf3c26977b7ab6 Mon Sep 17 00:00:00 2001
From: Star Richardson <67430892+alicenstar@users.noreply.github.com>
Date: Wed, 18 Oct 2023 14:41:25 -0600
Subject: [PATCH 15/17] add error handling, add mui react table to cluster
manage page
---
web/src/Root.tsx | 4 +-
web/src/components/UploadLicenseFile.tsx | 1 -
.../apps/HelmVMClusterManagement.tsx | 330 ++++++++++++------
web/src/components/apps/HelmVMNodeRow.jsx | 293 ----------------
web/src/components/apps/HelmVMViewNode.jsx | 10 +-
5 files changed, 229 insertions(+), 409 deletions(-)
delete mode 100644 web/src/components/apps/HelmVMNodeRow.jsx
diff --git a/web/src/Root.tsx b/web/src/Root.tsx
index f04f6951fd..2013369166 100644
--- a/web/src/Root.tsx
+++ b/web/src/Root.tsx
@@ -584,7 +584,7 @@ const Root = () => {
} />
{/* {state.adminConsoleMetadata?.isHelmVM && ( */}
{
{/* {(state.adminConsoleMetadata?.isKurl ||
state.adminConsoleMetadata?.isHelmVM) && ( */}
diff --git a/web/src/components/UploadLicenseFile.tsx b/web/src/components/UploadLicenseFile.tsx
index eabf2636f2..f2b6a9d500 100644
--- a/web/src/components/UploadLicenseFile.tsx
+++ b/web/src/components/UploadLicenseFile.tsx
@@ -271,7 +271,6 @@ const UploadLicenseFile = (props: Props) => {
navigate(`/${data.slug}/cluster/manage`, { replace: true });
return;
}
- // cluster manage -> config -> preflights
if (data.hasPreflight) {
navigate(`/${data.slug}/preflight`, { replace: true });
diff --git a/web/src/components/apps/HelmVMClusterManagement.tsx b/web/src/components/apps/HelmVMClusterManagement.tsx
index 457f92e93f..3670fd1805 100644
--- a/web/src/components/apps/HelmVMClusterManagement.tsx
+++ b/web/src/components/apps/HelmVMClusterManagement.tsx
@@ -1,8 +1,9 @@
import classNames from "classnames";
-import React, { ChangeEvent, useReducer, useState } from "react";
+import MaterialReactTable from "material-react-table";
+import React, { ChangeEvent, useMemo, useReducer, useState } from "react";
import Modal from "react-modal";
import { useQuery } from "react-query";
-import { useNavigate } from "react-router-dom";
+import { Link } from "react-router-dom";
import { KotsPageTitle } from "@components/Head";
import { useApps } from "@features/App";
@@ -11,8 +12,6 @@ import { Utilities } from "../../utilities/utilities";
import Icon from "../Icon";
import ErrorModal from "../modals/ErrorModal";
import CodeSnippet from "../shared/CodeSnippet";
-import Loader from "../shared/Loader";
-import HelmVMNodeRow from "./HelmVMNodeRow";
import "@src/scss/components/apps/HelmVMClusterManagement.scss";
@@ -94,7 +93,6 @@ const testData = {
type State = {
displayAddNode: boolean;
- selectedNodeType: string;
confirmDeleteNode: string;
deleteNodeError: string;
showConfirmDrainModal: boolean;
@@ -117,7 +115,6 @@ const HelmVMClusterManagement = ({
}),
{
displayAddNode: false,
- selectedNodeType: "primary",
confirmDeleteNode: "",
deleteNodeError: "",
showConfirmDrainModal: false,
@@ -127,12 +124,11 @@ const HelmVMClusterManagement = ({
}
);
const [selectedNodeTypes, setSelectedNodeTypes] = useState([]);
- const [useStaticToken, setUseStaticToken] = useState(false);
- const navigate = useNavigate();
+ const { data: appsData } = useApps();
+ const app = appsData?.apps?.find((a) => a.name === appName);
// #region queries
-
type NodesResponse = {
ha: boolean;
isHelmVMEnabled: boolean;
@@ -165,12 +161,11 @@ const HelmVMClusterManagement = ({
}[];
};
- const { data: nodesData, isInitialLoading: nodesLoading } = useQuery<
- NodesResponse,
- Error,
- NodesResponse,
- string
- >({
+ const {
+ data: nodesData,
+ isInitialLoading: nodesLoading,
+ error: nodesError,
+ } = useQuery({
queryKey: "helmVmNodes",
queryFn: async () => {
const res = await fetch(`${process.env.API_ENDPOINT}/helmvm/nodes`, {
@@ -180,53 +175,76 @@ const HelmVMClusterManagement = ({
credentials: "include",
method: "GET",
});
- if (res.status === 401) {
- Utilities.logoutUser();
+ if (!res.ok) {
+ if (res.status === 401) {
+ Utilities.logoutUser();
+ }
console.log(
"failed to get node status list, unexpected status code",
res.status
);
- const error = await res.json();
- throw new Error(
- error?.error?.message || error?.error || error?.message
- );
+ try {
+ const error = await res.json();
+ throw new Error(
+ error?.error?.message || error?.error || error?.message
+ );
+ } catch (err) {
+ throw new Error("Unable to fetch nodes, please try again later.");
+ }
}
return res.json();
},
- onSuccess: (data) => {
- setState({
- // if cluster doesn't support ha, then primary will be disabled. Force into secondary
- selectedNodeType: !data.ha ? "secondary" : state.selectedNodeType,
- });
- },
- refetchInterval: 1000,
+ refetchInterval: (data) => (data ? 1000 : 0),
retry: false,
});
+ type AddNodeCommandResponse = {
+ command: string;
+ expiry: string;
+ };
+
const {
data: generateAddNodeCommand,
isLoading: generateAddNodeCommandLoading,
- // error: generateAddNodeCommandError,
- } = useQuery({
+ error: generateAddNodeCommandError,
+ } = useQuery({
queryKey: ["generateAddNodeCommand", selectedNodeTypes],
queryFn: async ({ queryKey }) => {
- const [, selectedNodeTypes] = queryKey;
- return (
- await fetch(
- `${process.env.API_ENDPOINT}/helmvm/generate-node-join-command`,
- {
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
- credentials: "include",
- method: "POST",
- body: JSON.stringify({
- roles: selectedNodeTypes,
- }),
- }
- )
- ).json();
+ const [, nodeTypes] = queryKey;
+ const res = await fetch(
+ `${process.env.API_ENDPOINT}/helmvm/generate-node-join-command`,
+ {
+ headers: {
+ "Content-Type": "application/json",
+ Accept: "application/json",
+ },
+ credentials: "include",
+ method: "POST",
+ body: JSON.stringify({
+ roles: nodeTypes,
+ }),
+ }
+ );
+ if (!res.ok) {
+ if (res.status === 401) {
+ Utilities.logoutUser();
+ }
+ console.log(
+ "failed to get generate node command, unexpected status code",
+ res.status
+ );
+ try {
+ const error = await res.json();
+ throw new Error(
+ error?.error?.message || error?.error || error?.message
+ );
+ } catch (err) {
+ throw new Error(
+ "Unable to generate node join command, please try again later."
+ );
+ }
+ }
+ return res.json();
},
enabled: selectedNodeTypes.length > 0,
});
@@ -337,6 +355,7 @@ const HelmVMClusterManagement = ({
setState({ deleteNodeError: "" });
};
+ // #region node type logic
const NODE_TYPES = ["controller"];
const determineDisabledState = () => {
@@ -362,24 +381,87 @@ const HelmVMClusterManagement = ({
setSelectedNodeTypes([...types, nodeType]);
}
};
+ // #endregion
- const { data } = useApps();
-
- const handleContinue = () => {
- const app = data?.apps?.find((a) => a.name === appName);
- if (app?.isConfigurable) {
- navigate(`/${app.slug}/config`, { replace: true });
- return;
- }
- };
+ const columns = useMemo(
+ () => [
+ {
+ accessorKey: "name",
+ header: "Name",
+ enableHiding: false,
+ enableColumnDragging: false,
+ size: 150,
+ },
+ {
+ accessorKey: "roles",
+ header: "Role(s)",
+ size: 404,
+ },
+ {
+ accessorKey: "status",
+ header: "Status",
+ size: 150,
+ },
+ {
+ accessorKey: "disk",
+ header: "Disk",
+ size: 150,
+ },
+ {
+ accessorKey: "cpu",
+ header: "CPU",
+ size: 150,
+ },
+ {
+ accessorKey: "memory",
+ header: "Memory",
+ size: 150,
+ },
+ {
+ accessorKey: "pause",
+ header: "Pause",
+ size: 100,
+ },
+ {
+ accessorKey: "delete",
+ header: "Delete",
+ size: 100,
+ },
+ ],
+ []
+ );
- if (nodesLoading) {
- return (
-
-
-
- );
- }
+ const mappedNodes = useMemo(() => {
+ return (nodesData?.nodes || testData.nodes).map((n) => ({
+ name: n.name,
+ roles: (
+
+ {n.labels.map((l) => (
+
+ {l}
+
+ ))}
+
+ ),
+ status: n.isReady ? "Ready" : "Not Ready",
+ disk: n.conditions.diskPressure ? "Disk Pressure" : "No Disk Pressure",
+ cpu: n.conditions.pidPressure ? "CPU Pressure" : "No CPU Pressure",
+ memory: n.conditions.memoryPressure
+ ? "Memory Pressure"
+ : "No Memory Pressure",
+ pause: (
+ <>
+ Pause
+ >
+ ),
+ delete: (
+ <>
+ Delete
+ >
+ ),
+ }));
+ }, [nodesData?.nodes?.toString()]);
+ // #endregion
return (
@@ -399,32 +481,74 @@ const HelmVMClusterManagement = ({
className="btn primary tw-ml-auto tw-w-fit tw-h-fit"
onClick={onAddNodeClick}
>
- Add node type
+ Add node
)}
- {(nodesData?.nodes || testData?.nodes) &&
- (nodesData?.nodes || testData?.nodes).map((node, i) => (
-
- ))}
+ {nodesLoading && (
+
+ Loading nodes...
+
+ )}
+ {!nodesData && nodesError && (
+
+ {nodesError?.message}
+
+ )}
+ {(nodesData?.nodes || testData?.nodes) && (
+
+ )}
{fromLicenseFlow && (
-
Continue
-
+
)}
@@ -439,7 +563,7 @@ const HelmVMClusterManagement = ({
- Add a Node Type
+ Add a Node
- To add a node type to this cluster, select the type of node you are
- adding, and then select an installation method below. This screen
- will automatically show the status when the node successfully joins
- the cluster.
+ To add a node to this cluster, select the type of node you'd like to
+ add, and then select an installation method below. When the node
+ successfully joins the cluster, you will see it appear in the list
+ of nodes on this page.
{NODE_TYPES.map((nodeType) => (
@@ -460,7 +584,7 @@ const HelmVMClusterManagement = ({
key={nodeType}
className={classNames("BoxedCheckbox", {
"is-active": selectedNodeTypes.includes(nodeType),
- "is-disabled": determineDisabledState(nodeType),
+ "is-disabled": determineDisabledState(),
})}
>
@@ -483,7 +607,16 @@ const HelmVMClusterManagement = ({
))}
- {generateAddNodeCommandLoading &&
Generating command...
}
+ {generateAddNodeCommandLoading && (
+
+ Generating command...
+
+ )}
+ {!generateAddNodeCommand && generateAddNodeCommandError && (
+
+ {generateAddNodeCommandError?.message}
+
+ )}
{!generateAddNodeCommandLoading && generateAddNodeCommand?.command && (
)}
-
- setUseStaticToken(e.target.checked)}
- />
-
- Use a static token (useful for ASGs and scripts)
-
-
{/* buttons */}
Close
- setState({ displayAddNode: false })}
- >
- Add node type
-
diff --git a/web/src/components/apps/HelmVMNodeRow.jsx b/web/src/components/apps/HelmVMNodeRow.jsx
deleted file mode 100644
index 9386035869..0000000000
--- a/web/src/components/apps/HelmVMNodeRow.jsx
+++ /dev/null
@@ -1,293 +0,0 @@
-import classNames from "classnames";
-import React from "react";
-import { Link, useParams } from "react-router-dom";
-
-import { rbacRoles } from "../../constants/rbac";
-import { getPercentageStatus, Utilities } from "../../utilities/utilities";
-import Icon from "../Icon";
-import Loader from "../shared/Loader";
-
-export default function HelmVMNodeRow({
- node,
- drainNode,
- drainNodeSuccessful,
- drainingNodeName,
- deleteNode,
-}) {
- const { slug } = useParams();
-
- const DrainDeleteNode = () => {
- if (drainNode && Utilities.sessionRolesHasOneOf(rbacRoles.DRAIN_NODE)) {
- if (
- !drainNodeSuccessful &&
- drainingNodeName &&
- drainingNodeName === node?.name
- ) {
- return (
-
-
-
-
-
- Draining Node
-
-
- );
- } else if (drainNodeSuccessful && drainingNodeName === node?.name) {
- return (
-
-
-
- Node successfully drained
-
-
- );
- } else {
- return (
-
-
- node?.canDelete ? deleteNode(node?.name) : drainNode(node?.name)
- }
- className="btn secondary red"
- >
- {node?.canDelete ? "Delete node" : "Drain node"}
-
-
- );
- }
- }
- };
-
- return (
-
-
-
- {slug && (
-
- {node?.name}
-
- )}
- {!slug && (
-
- {node?.name}
-
- )}
- {node?.isPrimaryNode && (
-
- Primary node
-
- )}
-
-
-
-
-
- {node?.isConnected ? "Connected" : "Disconnected"}
-
-
-
-
-
-
-
-
- {node?.pods?.available === -1
- ? `${node?.pods?.capacity} pods`
- : `${
- node?.pods?.available === 0
- ? "0"
- : node?.pods?.capacity - node?.pods?.available
- } pods used`}
-
- {node?.pods?.available !== -1 && (
-
- of {node?.pods?.capacity} pods total
-
- )}
-
-
-
-
- {node?.cpu?.available === -1
- ? `${node?.cpu?.capacity} ${
- node?.cpu?.available === "1" ? "core" : "cores"
- }`
- : `${
- node?.cpu?.available === 0
- ? "0"
- : (node?.cpu?.capacity - node?.cpu?.available).toFixed(1)
- } ${
- node?.cpu?.available === "1" ? "core used" : "cores used"
- }`}
-
- {node?.pods?.available !== -1 && (
-
- of {node?.cpu?.capacity}{" "}
- {node?.cpu?.available === "1" ? "core total" : "cores total"}
-
- )}
-
-
-
-
- {node?.memory?.available === -1
- ? `${node?.memory?.capacity?.toFixed(1)} GB`
- : `${
- node?.memory?.available === 0
- ? "0"
- : (
- node?.memory?.capacity - node?.memory?.available
- ).toFixed(1)
- } GB used`}
-
- {node?.pods?.available !== -1 && (
-
- of {node?.memory?.capacity?.toFixed(1)} GB total
-
- )}
-
-
-
-
-
-
- {node?.kubeletVersion}
-
-
-
-
-
- {node?.conditions?.diskPressure
- ? "No Space on Device"
- : "No Disk Pressure"}
-
-
-
-
-
- {node?.conditions?.pidPressure
- ? "Pressure on CPU"
- : "No CPU Pressure"}
-
-
-
-
-
- {node?.conditions?.memoryPressure
- ? "No Space on Memory"
- : "No Memory Pressure"}
-
-
-
- {/* LABELS */}
-
- {node?.labels.length > 0
- ? node.labels.sort().map((label, i) => {
- let labelToShow = label.replace(":", "=");
- return (
-
- {labelToShow}
-
- );
- })
- : null}
-
-
-
- For more details run{" "}
-
- kubectl describe node {node?.name}
-
-
-
-
-
-
- );
-}
diff --git a/web/src/components/apps/HelmVMViewNode.jsx b/web/src/components/apps/HelmVMViewNode.jsx
index a72f13648f..a9b8bae245 100644
--- a/web/src/components/apps/HelmVMViewNode.jsx
+++ b/web/src/components/apps/HelmVMViewNode.jsx
@@ -308,6 +308,7 @@ const HelmVMViewNode = () => {
const node = nodeData || testData.nodes[0];
+ // #region table data
const columns = useMemo(
() => [
{
@@ -347,9 +348,9 @@ const HelmVMViewNode = () => {
);
const mappedPods = useMemo(() => {
- return node.podList.map((n) => ({
- name: n.metadata.name,
- status: n.status.phase,
+ return node?.podList?.map((p) => ({
+ name: p.metadata.name,
+ status: p.status.phase,
disk: null,
cpu: null,
memory: null,
@@ -359,7 +360,8 @@ const HelmVMViewNode = () => {
>
),
}));
- }, [node.podList]);
+ }, [node?.podList?.toString()]);
+ // #endregion
return (
From ccfa67fd1a3070298cc09f5107a7d83f144cc49a Mon Sep 17 00:00:00 2001
From: Star Richardson <67430892+alicenstar@users.noreply.github.com>
Date: Wed, 18 Oct 2023 14:43:03 -0600
Subject: [PATCH 16/17] move ts-ignore line
---
web/src/components/UploadLicenseFile.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/web/src/components/UploadLicenseFile.tsx b/web/src/components/UploadLicenseFile.tsx
index f2b6a9d500..2fe1ed8a12 100644
--- a/web/src/components/UploadLicenseFile.tsx
+++ b/web/src/components/UploadLicenseFile.tsx
@@ -1,11 +1,11 @@
import React, { useEffect, useReducer } from "react";
import { Link, useNavigate } from "react-router-dom";
-// TODO: upgrade this dependency
-// @ts-ignore
import yaml from "js-yaml";
import isEmpty from "lodash/isEmpty";
import keyBy from "lodash/keyBy";
import size from "lodash/size";
+// TODO: upgrade this dependency
+// @ts-ignore
import Dropzone from "react-dropzone";
import Modal from "react-modal";
import Select from "react-select";
From dd46762876ff48d2e85e6598d45523ac3512139c Mon Sep 17 00:00:00 2001
From: Star Richardson <67430892+alicenstar@users.noreply.github.com>
Date: Wed, 18 Oct 2023 15:26:10 -0600
Subject: [PATCH 17/17] remove delete functionality for now
---
.../apps/HelmVMClusterManagement.tsx | 193 ++----------------
1 file changed, 18 insertions(+), 175 deletions(-)
diff --git a/web/src/components/apps/HelmVMClusterManagement.tsx b/web/src/components/apps/HelmVMClusterManagement.tsx
index 3670fd1805..ed0fcfa3f2 100644
--- a/web/src/components/apps/HelmVMClusterManagement.tsx
+++ b/web/src/components/apps/HelmVMClusterManagement.tsx
@@ -3,14 +3,13 @@ import MaterialReactTable from "material-react-table";
import React, { ChangeEvent, useMemo, useReducer, useState } from "react";
import Modal from "react-modal";
import { useQuery } from "react-query";
-import { Link } from "react-router-dom";
+import { Link, useParams } from "react-router-dom";
import { KotsPageTitle } from "@components/Head";
import { useApps } from "@features/App";
import { rbacRoles } from "../../constants/rbac";
import { Utilities } from "../../utilities/utilities";
import Icon from "../Icon";
-import ErrorModal from "../modals/ErrorModal";
import CodeSnippet from "../shared/CodeSnippet";
import "@src/scss/components/apps/HelmVMClusterManagement.scss";
@@ -127,6 +126,7 @@ const HelmVMClusterManagement = ({
const { data: appsData } = useApps();
const app = appsData?.apps?.find((a) => a.name === appName);
+ const { slug } = useParams();
// #region queries
type NodesResponse = {
@@ -270,91 +270,12 @@ const HelmVMClusterManagement = ({
// });
// #endregion
- const deleteNode = (name: string) => {
- setState({
- confirmDeleteNode: name,
- });
- };
-
- const cancelDeleteNode = () => {
- setState({
- confirmDeleteNode: "",
- });
- };
-
- const reallyDeleteNode = () => {
- const name = state.confirmDeleteNode;
- cancelDeleteNode();
-
- fetch(`${process.env.API_ENDPOINT}/helmvm/nodes/${name}`, {
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
- credentials: "include",
- method: "DELETE",
- })
- .then(async (res) => {
- if (!res.ok) {
- if (res.status === 401) {
- Utilities.logoutUser();
- return;
- }
- setState({
- deleteNodeError: `Delete failed with status ${res.status}`,
- });
- }
- })
- .catch((err) => {
- console.log(err);
- });
- };
-
- const onDrainNodeClick = (name: string) => {
- setState({
- showConfirmDrainModal: true,
- nodeNameToDrain: name,
- });
- };
-
- const drainNode = async (name: string) => {
- setState({ showConfirmDrainModal: false, drainingNodeName: name });
- fetch(`${process.env.API_ENDPOINT}/helmvm/nodes/${name}/drain`, {
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
- credentials: "include",
- method: "POST",
- })
- .then(async () => {
- setState({ drainNodeSuccessful: true });
- setTimeout(() => {
- setState({
- drainingNodeName: null,
- drainNodeSuccessful: false,
- });
- }, 3000);
- })
- .catch((err) => {
- console.log(err);
- setState({
- drainingNodeName: null,
- drainNodeSuccessful: false,
- });
- });
- };
-
const onAddNodeClick = () => {
setState({
displayAddNode: true,
});
};
- const ackDeleteNodeError = () => {
- setState({ deleteNodeError: "" });
- };
-
// #region node type logic
const NODE_TYPES = ["controller"];
@@ -433,7 +354,14 @@ const HelmVMClusterManagement = ({
const mappedNodes = useMemo(() => {
return (nodesData?.nodes || testData.nodes).map((n) => ({
- name: n.name,
+ name: slug ? (
+
+ ) : (
+ n.name
+ ),
roles: (
{n.labels.map((l) => (
@@ -472,8 +400,8 @@ const HelmVMClusterManagement = ({
Cluster Nodes
-
- This section lists the nodes that are configured and shows the
+
+ This page lists the nodes that are configured and shows the
status/health of each.
{Utilities.sessionRolesHasOneOf([rbacRoles.CLUSTER_ADMIN]) && (
@@ -560,7 +488,7 @@ const HelmVMClusterManagement = ({
className="Modal"
ariaHideApp={false}
>
-
+
Add a Node
@@ -572,11 +500,12 @@ const HelmVMClusterManagement = ({
onClick={() => setState({ displayAddNode: false })}
/>
-
+
To add a node to this cluster, select the type of node you'd like to
- add, and then select an installation method below. When the node
- successfully joins the cluster, you will see it appear in the list
- of nodes on this page.
+ add. Once you've selected a node type, we will generate a node join
+ command for you to use in the CLI. When the node successfully joins
+ the cluster, you will see it appear in the list of nodes on this
+ page.
{NODE_TYPES.map((nodeType) => (
@@ -641,92 +570,6 @@ const HelmVMClusterManagement = ({
- {state.deleteNodeError && (
-
- )}
-
-
-
- Deleting this node may cause data loss. Are you sure you want to
- proceed?
-
-
-
- Delete {state.confirmDeleteNode}
-
-
- Cancel
-
-
-
-
- {state.showConfirmDrainModal && (
-
- setState({
- showConfirmDrainModal: false,
- nodeNameToDrain: "",
- })
- }
- shouldReturnFocusAfterClose={false}
- contentLabel="Confirm Drain Node"
- ariaHideApp={false}
- className="Modal MediumSize"
- >
-
-
- Are you sure you want to drain {state.nodeNameToDrain}?
-
-
- Draining this node may cause data loss. If you want to delete{" "}
- {state.nodeNameToDrain} you must disconnect it after it has been
- drained.
-
-
- drainNode(state.nodeNameToDrain)}
- type="button"
- className="btn red primary"
- >
- Drain {state.nodeNameToDrain}
-
-
- setState({
- showConfirmDrainModal: false,
- nodeNameToDrain: "",
- })
- }
- type="button"
- className="btn secondary u-marginLeft--20"
- >
- Cancel
-
-
-
-
- )}
);
};