) => (
-
- {containers.map(({ name, state, status }) => (
-
- {/* wrapper */}
-
-
-
-
- ))}
-
- )
+ render: (_, pod) => {
+ const containers = pod.getContainers().map((container) => {
+ const status = pod.getContainerStatuses().find((status) => status.name === container.name);
+ const state = status ? keys(status?.state ?? {})[0] : '';
+ return {
+ name: container.name,
+ state,
+ status
+ };
+ });
+ return (
+
+ {containers.map(({ name, state, status }) => (
+
+ {/* wrapper */}
+
+
+
+
+ ))}
+
+ );
+ }
},
{
title: 'Restarts',
- dataIndex: 'restarts',
- key: 'restarts'
+ key: 'restarts',
+ render: (_, pod) => pod.getRestartsCount()
},
{
title: 'Controlled By',
- dataIndex: 'ownerRefs',
key: 'controlled-by',
- render: (ownerRefs: Array<{ name: string; kind: string }>) =>
- ownerRefs.map(({ name, kind }) => {kind})
+ render: (_, pod) => pod.getOwnerRefs().map(({ name, kind }) => {kind})
},
{
title: 'QoS',
@@ -104,26 +70,24 @@ const columns: ColumnsType = [
title: 'Age',
dataIndex: 'creationTimestamp',
key: 'age',
- render: (creationTimestamp: string) =>
+ render: (_, pod) =>
},
{
title: 'Status',
- dataIndex: 'status',
fixed: 'right',
key: 'status',
filters: PodStatusMessage.map((value) => ({ text: value, value })),
- onFilter: (value, record) => record.status === value,
- render: (status: string) =>
+ onFilter: (value, pod) => pod.getStatusMessage() === value,
+ render: (_, pod) =>
},
{
key: 'action',
- dataIndex: 'name',
fixed: 'right',
- render: (name: string) => (
+ render: (_, pod) => (
pod.getName() === name)[0]}
- onDelete={() => deleteResource(name, Resources.Pods)}
- onUpdate={(data: string) => updateResource(data, name, Resources.Pods)}
+ obj={pod}
+ onDelete={() => deleteResource(pod.getName(), Resources.Pods)}
+ onUpdate={(data: string) => updateResource(data, pod.getName(), Resources.Pods)}
/>
)
}
@@ -132,22 +96,20 @@ const columns: ColumnsType = [
const PodOverviewPage = () => {
const [openDrawer, setOpenDrawer] = useState(false);
const [pod, setPod] = useState();
+ const { items, replace } = usePodStore();
- useQuery(['pods'], () => POD_STORE.fetchData(), {
+ useQuery(['pods'], () => fetchData(replace, Resources.Pods), {
refetchInterval: 5000
});
- const dataSource = POD_STORE.items.map(getData);
return (
<>
({
+ dataSource={items}
+ onRow={(pod) => ({
onClick: () => {
- const { key } = record;
- const pod = POD_STORE.items.filter((pod) => pod.getName() === key)[0];
setPod(pod);
setOpenDrawer(true);
}
@@ -158,4 +120,4 @@ const PodOverviewPage = () => {
);
};
-export default observer(PodOverviewPage);
+export default PodOverviewPage;
diff --git a/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/statefulset/statefulset.tsx b/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/statefulset/statefulset.tsx
index 4f836f26086..8f21e4632d4 100644
--- a/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/statefulset/statefulset.tsx
+++ b/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/statefulset/statefulset.tsx
@@ -1,9 +1,7 @@
import { KubeObjectAge } from '@/components/kube/object/kube-object-age';
import { StatefulSet } from '@/k8slens/kube-object';
-import { POD_STORE, STATEFUL_SET_STORE } from '@/store/static';
import { useQuery } from '@tanstack/react-query';
import { ColumnsType } from 'antd/es/table';
-import { observer } from 'mobx-react';
import { useRef, useState } from 'react';
import StatefulSetDetail from './statefulset-detail';
import { RequestController } from '@/utils/request-controller';
@@ -12,57 +10,41 @@ import ActionButton from '../../../action-button/action-button';
import { deleteResource } from '@/api/delete';
import { Resources } from '@/constants/kube-object';
import { updateResource } from '@/api/update';
+import { fetchData, getPodsByOwnerId, usePodStore, useStatefulSetStore } from '@/store/kube';
-interface DataType {
- key: string;
- name: string;
- pods: string;
- replicas: number;
- creationTimestamp?: string;
-}
-
-const getData = (statefulSet: StatefulSet): DataType => {
- const { readyReplicas = 0, currentReplicas = 0, replicas = 0 } = statefulSet.status ?? {};
- return {
- key: statefulSet.getName(),
- name: statefulSet.getName(),
- pods: `${readyReplicas}/${replicas}`,
- replicas: statefulSet.getReplicas(),
- creationTimestamp: statefulSet.metadata.creationTimestamp
- };
-};
-
-const columns: ColumnsType = [
+const columns: ColumnsType = [
{
title: 'Name',
- dataIndex: 'name',
- key: 'name'
+ key: 'name',
+ fixed: 'left',
+ render: (_, stat) => stat.getName()
},
{
title: 'Pods',
- dataIndex: 'pods',
- key: 'pods'
+ key: 'pods',
+ render: (_, stat) => {
+ const { readyReplicas = 0, replicas = 0 } = stat.status ?? {};
+ return `${readyReplicas}/${replicas}`;
+ }
},
{
title: 'Replicas',
- dataIndex: 'replicas',
- key: 'replicas'
+ key: 'replicas',
+ render: (_, stat) => stat.getReplicas()
},
{
title: 'Age',
- dataIndex: 'creationTimestamp',
key: 'age',
- render: (creationTimestamp: string) =>
+ render: (_, stat) =>
},
{
- dataIndex: 'name',
key: 'action',
fixed: 'right',
- render: (name: string) => (
+ render: (_, stat) => (
statefulSet.getName() === name)[0]}
- onUpdate={(data: string) => updateResource(data, name, Resources.StatefulSets)}
- onDelete={() => deleteResource(name, Resources.StatefulSets)}
+ obj={stat}
+ onUpdate={(data: string) => updateResource(data, stat.getName(), Resources.StatefulSets)}
+ onDelete={() => deleteResource(stat.getName(), Resources.StatefulSets)}
/>
)
}
@@ -70,13 +52,18 @@ const columns: ColumnsType = [
const StatefulSetOverviewPage = () => {
const [openDrawer, setOpenDrawer] = useState(false);
- const [statefulSet, setStatefulSet] = useState();
+ const [stat, setStat] = useState();
+ const { items: pods, replace: replacePods } = usePodStore();
+ const { items: stats, replace: replaceStats } = useStatefulSetStore();
const requestController = useRef(new RequestController({ timeoutDuration: 5000 }));
useQuery(
['statefulSets', 'pods'],
() => {
- const task = [STATEFUL_SET_STORE.fetchData, POD_STORE.fetchData];
+ const task = [
+ () => fetchData(replaceStats, Resources.StatefulSets),
+ () => fetchData(replacePods, Resources.Pods)
+ ];
return requestController.current.runTasks(task);
},
{
@@ -84,27 +71,22 @@ const StatefulSetOverviewPage = () => {
}
);
- const dataSource = STATEFUL_SET_STORE.items.map(getData);
return (
<>
({
+ dataSource={stats}
+ onRow={(stat) => ({
onClick: () => {
- const { key } = record;
- const statefulSet = STATEFUL_SET_STORE.items.filter(
- (statefulSet) => statefulSet.getName() === key
- )[0];
- setStatefulSet(statefulSet);
+ setStat(stat);
setOpenDrawer(true);
}
})}
/>
setOpenDrawer(false)}
/>
@@ -112,4 +94,4 @@ const StatefulSetOverviewPage = () => {
);
};
-export default observer(StatefulSetOverviewPage);
+export default StatefulSetOverviewPage;
diff --git a/frontend/plugins/kubepanel/src/pages/kubepanel/components/overview.tsx b/frontend/plugins/kubepanel/src/pages/kubepanel/components/overview.tsx
deleted file mode 100644
index b7eaf83086b..00000000000
--- a/frontend/plugins/kubepanel/src/pages/kubepanel/components/overview.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import AppLayout from './layout';
-import OverviewPage from './kube-object/workload/overview/overview';
-import { useState } from 'react';
-import { SideNavItemKey } from './sidebar/sidebar';
-import PodOverviewPage from './kube-object/workload/pod/pod';
-import DeploymentOverviewPage from './kube-object/workload/deployment/deployment';
-import StatefulSetOverviewPage from './kube-object/workload/statefulset/statefulset';
-import ConfigMapOverviewPage from './kube-object/config/config-map/config-map';
-import PersistentVolumeClaimOverviewPage from './kube-object/storage/volume-claim/volume-claim';
-import { FloatButton } from 'antd';
-import CreateResourceModal from './modal/create-resource-modal';
-import { PlusOutlined } from '@ant-design/icons';
-
-const switchPage = (key: SideNavItemKey): React.ReactNode => {
- switch (key) {
- case SideNavItemKey.Overview:
- return ;
- case SideNavItemKey.Pod:
- return ;
- case SideNavItemKey.Deployment:
- return ;
- case SideNavItemKey.StatefulSet:
- return ;
- case SideNavItemKey.ConfigMap:
- return ;
- case SideNavItemKey.PersistentVolumeClaim:
- return ;
- default:
- return ;
- }
-};
-
-const Home = () => {
- const [sideNavItemKey, setSideNavItemKey] = useState(SideNavItemKey.Overview);
- const [openCreateResourceModal, setOpenCreateResourceModal] = useState(false);
-
- return (
- setSideNavItemKey(key)}>
- {switchPage(sideNavItemKey)}
- }
- type="primary"
- onClick={() => setOpenCreateResourceModal(true)}
- />
- setOpenCreateResourceModal(false)}
- />
-
- );
-};
-
-export default Home;
diff --git a/frontend/plugins/kubepanel/src/pages/kubepanel/index.tsx b/frontend/plugins/kubepanel/src/pages/kubepanel/index.tsx
index f48356d0ae6..a8e692fbab9 100644
--- a/frontend/plugins/kubepanel/src/pages/kubepanel/index.tsx
+++ b/frontend/plugins/kubepanel/src/pages/kubepanel/index.tsx
@@ -1,55 +1,53 @@
-import { useLoading } from '@/hooks/useLoading';
-import { useToast } from '@/hooks/useToast';
-import {
- CONFIG_MAP_STORE,
- DEPLOYMENT_STORE,
- PERSISTENT_VOLUME_CLAIM_STORE,
- POD_STORE,
- STATEFUL_SET_STORE
-} from '@/store/static';
-import { RequestController } from '@/utils/request-controller';
-import { CreateToastFnReturn } from '@chakra-ui/react';
-import { isError } from 'lodash';
-import { useEffect, useRef } from 'react';
-import OverviewPage from './components/overview';
-import { observer } from 'mobx-react';
+import AppLayout from './components/layout';
+import OverviewPage from './components/kube-object/workload/overview/overview';
+import { useState } from 'react';
+import { SideNavItemKey } from './components/sidebar/sidebar';
+import PodOverviewPage from './components/kube-object/workload/pod/pod';
+import DeploymentOverviewPage from './components/kube-object/workload/deployment/deployment';
+import StatefulSetOverviewPage from './components/kube-object/workload/statefulset/statefulset';
+import ConfigMapOverviewPage from './components/kube-object/config/config-map/config-map';
+import PersistentVolumeClaimOverviewPage from './components/kube-object/storage/volume-claim/volume-claim';
+import { FloatButton } from 'antd';
+import CreateResourceModal from './components/modal/create-resource-modal';
+import { PlusOutlined } from '@ant-design/icons';
-const Home = observer(() => {
- const { isLoading, setIsLoading, Loading } = useLoading();
- const { toast } = useToast();
-
- const requestController = useRef(new RequestController({ timeoutDuration: 5000 }));
-
- useEffect(() => {
- (async () => {
- const res = await fetchData(requestController.current);
- toastErrors(res, toast);
- setIsLoading(false);
- })();
- });
-
- return <>{isLoading ? : }>;
-});
-
-const fetchData = (requestController: RequestController) => {
- const tasks = [
- POD_STORE.fetchData,
- DEPLOYMENT_STORE.fetchData,
- STATEFUL_SET_STORE.fetchData,
- PERSISTENT_VOLUME_CLAIM_STORE.fetchData,
- CONFIG_MAP_STORE.fetchData
- ];
- return requestController.runTasks(tasks);
+const switchPage = (key: SideNavItemKey): React.ReactNode => {
+ switch (key) {
+ case SideNavItemKey.Overview:
+ return ;
+ case SideNavItemKey.Pod:
+ return ;
+ case SideNavItemKey.Deployment:
+ return ;
+ case SideNavItemKey.StatefulSet:
+ return ;
+ case SideNavItemKey.ConfigMap:
+ return ;
+ case SideNavItemKey.PersistentVolumeClaim:
+ return ;
+ default:
+ return ;
+ }
};
-const toastErrors = (res: any[], toast: CreateToastFnReturn) => {
- if (isError(res)) {
- toast({
- title: 'Error',
- description: res.message,
- status: 'error'
- });
- }
+const Home = () => {
+ const [sideNavItemKey, setSideNavItemKey] = useState(SideNavItemKey.Overview);
+ const [openCreateResourceModal, setOpenCreateResourceModal] = useState(false);
+
+ return (
+ setSideNavItemKey(key)}>
+ {switchPage(sideNavItemKey)}
+ }
+ type="primary"
+ onClick={() => setOpenCreateResourceModal(true)}
+ />
+ setOpenCreateResourceModal(false)}
+ />
+
+ );
};
export default Home;
diff --git a/frontend/plugins/kubepanel/src/store/k8s/config-map.store.ts b/frontend/plugins/kubepanel/src/store/k8s/config-map.store.ts
new file mode 100644
index 00000000000..15c40351cd2
--- /dev/null
+++ b/frontend/plugins/kubepanel/src/store/k8s/config-map.store.ts
@@ -0,0 +1,8 @@
+import { ConfigMap } from '@/k8slens/kube-object';
+import { create } from 'zustand';
+import { createKubeStoreSlice } from './kube.store';
+import { ConfigMapStore } from '@/types/state';
+
+export const useConfigMapStore = create()((...a) => ({
+ ...createKubeStoreSlice(ConfigMap.kind)(...a)
+}));
diff --git a/frontend/plugins/kubepanel/src/store/k8s/configmap.store.ts b/frontend/plugins/kubepanel/src/store/k8s/configmap.store.ts
deleted file mode 100644
index 542d1e97536..00000000000
--- a/frontend/plugins/kubepanel/src/store/k8s/configmap.store.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { ConfigMap } from '@/k8slens/kube-object';
-import { ItemStore } from './data.store';
-import { Resources } from '@/constants/kube-object';
-
-export class ConfigMapStore extends ItemStore {
- constructor() {
- super(Resources.ConfigMaps);
- }
-}
diff --git a/frontend/plugins/kubepanel/src/store/k8s/data.store.ts b/frontend/plugins/kubepanel/src/store/k8s/data.store.ts
deleted file mode 100644
index 84121072e5c..00000000000
--- a/frontend/plugins/kubepanel/src/store/k8s/data.store.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import { entries, isArray } from 'lodash';
-import { action, makeObservable, observable } from 'mobx';
-import { KubeObject } from '@/k8slens/kube-object';
-import { Resources } from '@/constants/kube-object';
-import { getResource } from '@/api/list';
-
-export class ItemStore {
- @observable items = observable.array([], { deep: false });
- private readonly resource: Resources;
-
- constructor(resource: Resources) {
- this.resource = resource;
- makeObservable(this);
- }
-
- @action
- replaceItems(items: K | Array) {
- if (isArray(items)) {
- this.items.replace(items);
- } else {
- this.items.replace([items]);
- }
- }
-
- getByLabel(labels: string[] | Partial>): K[] {
- if (Array.isArray(labels)) {
- return this.items.filter((item: K) => {
- const itemLabels = item.getLabels();
-
- return labels.every((label) => itemLabels.includes(label));
- });
- } else {
- return this.items.filter((item: K) => {
- const itemLabels = item.metadata.labels || {};
-
- return entries(labels).every(([key, value]) => itemLabels[key] === value);
- });
- }
- }
-
- // function() can't be used as a variable, it'll cause an 'This is undefined' error
- // we want to consider it as a variable and pass it into RequestController.runTasks
- fetchData = async () => {
- try {
- const items = await getResource(this.resource);
- this.replaceItems(items);
- return items;
- } catch (err) {
- return Promise.reject(err);
- }
- };
-}
diff --git a/frontend/plugins/kubepanel/src/store/k8s/deployment.store.ts b/frontend/plugins/kubepanel/src/store/k8s/deployment.store.ts
index 6c5ad66165d..bfeb13ed028 100644
--- a/frontend/plugins/kubepanel/src/store/k8s/deployment.store.ts
+++ b/frontend/plugins/kubepanel/src/store/k8s/deployment.store.ts
@@ -1,34 +1,28 @@
-import { Deployment, PodStatusPhase } from '@/k8slens/kube-object';
-import { GetByLabel } from './pod.store';
-import { ItemStore } from './data.store';
-import { Resources } from '@/constants/kube-object';
+import { Deployment, Pod, PodStatusPhase } from '@/k8slens/kube-object';
+import { createKubeStoreSlice, getByLabel } from './kube.store';
+import { DeploymentStore } from '@/types/state';
+import { create } from 'zustand';
-const getChildPods = (getByLabel: GetByLabel, deployment: Deployment) => {
- return getByLabel(deployment.getTemplateLabels()).filter(
- (pod) => pod.getNs() === deployment.getNs()
- );
-};
+export function getDeploymentsStatuses(deps: Deployment[], pods: Pod[]) {
+ const status = { running: 0, failed: 0, pending: 0 };
-export class DeploymentStore extends ItemStore {
- constructor() {
- super(Resources.Deployments);
- }
+ deps.forEach((deployment) => {
+ const statuses = new Set(
+ getByLabel(pods, deployment.getTemplateLabels()).map((pod) => pod.getStatus())
+ );
- getDeploymentsStatuses(getByLabel: GetByLabel) {
- const status = { running: 0, failed: 0, pending: 0 };
+ if (statuses.has(PodStatusPhase.FAILED)) {
+ status.failed++;
+ } else if (statuses.has(PodStatusPhase.PENDING)) {
+ status.pending++;
+ } else {
+ status.running++;
+ }
+ });
- this.items.forEach((deployment) => {
- const statuses = new Set(getChildPods(getByLabel, deployment).map((pod) => pod.getStatus()));
-
- if (statuses.has(PodStatusPhase.FAILED)) {
- status.failed++;
- } else if (statuses.has(PodStatusPhase.PENDING)) {
- status.pending++;
- } else {
- status.running++;
- }
- });
-
- return status;
- }
+ return status;
}
+
+export const useDeploymentStore = create()((...a) => ({
+ ...createKubeStoreSlice(Deployment.kind)(...a)
+}));
diff --git a/frontend/plugins/kubepanel/src/store/k8s/kube.store.ts b/frontend/plugins/kubepanel/src/store/k8s/kube.store.ts
index 11c41fd8cfc..a034295bae2 100644
--- a/frontend/plugins/kubepanel/src/store/k8s/kube.store.ts
+++ b/frontend/plugins/kubepanel/src/store/k8s/kube.store.ts
@@ -1,26 +1,69 @@
+import { getResource } from '@/api/list';
+import { Resources } from '@/constants/kube-object';
import { KubeObject } from '@/k8slens/kube-object';
-import { randomUUID } from 'crypto';
-import { observable } from 'mobx';
+import { KubeStore } from '@/types/state';
+import { entries } from 'lodash';
+import { StateCreator } from 'zustand';
-type cmp