From 8777d5982285b7cde31ad9669dba507559375146 Mon Sep 17 00:00:00 2001 From: warjiang <1096409085@qq.com> Date: Thu, 15 Aug 2024 11:22:35 +0800 Subject: [PATCH] implement statefulset resources (#65) * feat: not allow wrap for small device Signed-off-by: warjiang <1096409085@qq.com> * feat: api for statefulset Signed-off-by: warjiang <1096409085@qq.com> * feat: opt label view for workloads Signed-off-by: warjiang <1096409085@qq.com> * feat: add search filter for workload; add statefulset implementation Signed-off-by: warjiang <1096409085@qq.com> --------- Signed-off-by: warjiang <1096409085@qq.com> --- cmd/api/app/api.go | 1 + cmd/api/app/routes/statefulset/handler.go | 54 ++++++++ pkg/resource/common/resourcechannels.go | 27 ++++ pkg/resource/statefulset/common.go | 66 ++++++++++ pkg/resource/statefulset/detail.go | 47 +++++++ pkg/resource/statefulset/list.go | 110 ++++++++++++++++ pkg/resource/statefulset/pods.go | 40 ++++++ .../src/components/tag-list/index.tsx | 15 ++- .../workload/index.tsx | 124 ++++++++++++++---- .../workload/workload-detail-drawer.tsx | 9 +- ui/apps/dashboard/src/services/base.ts | 6 + ui/apps/dashboard/src/services/workload.ts | 45 +++++-- 12 files changed, 501 insertions(+), 43 deletions(-) create mode 100644 cmd/api/app/routes/statefulset/handler.go create mode 100644 pkg/resource/statefulset/common.go create mode 100644 pkg/resource/statefulset/detail.go create mode 100644 pkg/resource/statefulset/list.go create mode 100644 pkg/resource/statefulset/pods.go diff --git a/cmd/api/app/api.go b/cmd/api/app/api.go index 3616d6b1..5ce0e458 100644 --- a/cmd/api/app/api.go +++ b/cmd/api/app/api.go @@ -21,6 +21,7 @@ import ( _ "github.com/karmada-io/dashboard/cmd/api/app/routes/namespace" _ "github.com/karmada-io/dashboard/cmd/api/app/routes/overview" _ "github.com/karmada-io/dashboard/cmd/api/app/routes/propagationpolicy" + _ "github.com/karmada-io/dashboard/cmd/api/app/routes/statefulset" _ "github.com/karmada-io/dashboard/cmd/api/app/routes/unstructured" ) diff --git a/cmd/api/app/routes/statefulset/handler.go b/cmd/api/app/routes/statefulset/handler.go new file mode 100644 index 00000000..9ee07ef7 --- /dev/null +++ b/cmd/api/app/routes/statefulset/handler.go @@ -0,0 +1,54 @@ +package deployment + +import ( + "github.com/gin-gonic/gin" + "github.com/karmada-io/dashboard/cmd/api/app/router" + "github.com/karmada-io/dashboard/cmd/api/app/types/common" + "github.com/karmada-io/dashboard/pkg/client" + "github.com/karmada-io/dashboard/pkg/resource/event" + "github.com/karmada-io/dashboard/pkg/resource/statefulset" +) + +func handleGetStatefulsets(c *gin.Context) { + namespace := common.ParseNamespacePathParameter(c) + dataSelect := common.ParseDataSelectPathParameter(c) + k8sClient := client.InClusterClientForKarmadaApiServer() + result, err := statefulset.GetStatefulSetList(k8sClient, namespace, dataSelect) + if err != nil { + common.Fail(c, err) + return + } + common.Success(c, result) +} + +func handleGetStatefulsetDetail(c *gin.Context) { + namespace := c.Param("namespace") + name := c.Param("statefulset") + k8sClient := client.InClusterClientForKarmadaApiServer() + result, err := statefulset.GetStatefulSetDetail(k8sClient, namespace, name) + if err != nil { + common.Fail(c, err) + return + } + common.Success(c, result) +} + +func handleGetStatefulsetEvents(c *gin.Context) { + namespace := c.Param("namespace") + name := c.Param("statefulset") + k8sClient := client.InClusterClientForKarmadaApiServer() + dataSelect := common.ParseDataSelectPathParameter(c) + result, err := event.GetResourceEvents(k8sClient, dataSelect, namespace, name) + if err != nil { + common.Fail(c, err) + return + } + common.Success(c, result) +} +func init() { + r := router.V1() + r.GET("/statefulset", handleGetStatefulsets) + r.GET("/statefulset/:namespace", handleGetStatefulsets) + r.GET("/statefulset/:namespace/:statefulset", handleGetStatefulsetDetail) + r.GET("/statefulset/:namespace/:statefulset/event", handleGetStatefulsetEvents) +} diff --git a/pkg/resource/common/resourcechannels.go b/pkg/resource/common/resourcechannels.go index 117a6201..472abf47 100644 --- a/pkg/resource/common/resourcechannels.go +++ b/pkg/resource/common/resourcechannels.go @@ -329,6 +329,33 @@ type StatefulSetListChannel struct { Error chan error } +// GetStatefulSetListChannel returns a pair of channels to a StatefulSet list and errors that both must be read +// numReads times. +func GetStatefulSetListChannel(client client.Interface, + nsQuery *NamespaceQuery, numReads int) StatefulSetListChannel { + channel := StatefulSetListChannel{ + List: make(chan *apps.StatefulSetList, numReads), + Error: make(chan error, numReads), + } + + go func() { + statefulSets, err := client.AppsV1().StatefulSets(nsQuery.ToRequestParam()).List(context.TODO(), helpers.ListEverything) + var filteredItems []apps.StatefulSet + for _, item := range statefulSets.Items { + if nsQuery.Matches(item.ObjectMeta.Namespace) { + filteredItems = append(filteredItems, item) + } + } + statefulSets.Items = filteredItems + for i := 0; i < numReads; i++ { + channel.List <- statefulSets + channel.Error <- err + } + }() + + return channel +} + // ConfigMapListChannel is a list and error channels to ConfigMaps. type ConfigMapListChannel struct { List chan *v1.ConfigMapList diff --git a/pkg/resource/statefulset/common.go b/pkg/resource/statefulset/common.go new file mode 100644 index 00000000..769679af --- /dev/null +++ b/pkg/resource/statefulset/common.go @@ -0,0 +1,66 @@ +package statefulset + +import ( + "github.com/karmada-io/dashboard/pkg/dataselect" + "github.com/karmada-io/dashboard/pkg/resource/common" + "github.com/karmada-io/dashboard/pkg/resource/event" + apps "k8s.io/api/apps/v1" + v1 "k8s.io/api/core/v1" +) + +// The code below allows to perform complex data section on []apps.StatefulSet + +type StatefulSetCell apps.StatefulSet + +func (self StatefulSetCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue { + switch name { + case dataselect.NameProperty: + return dataselect.StdComparableString(self.ObjectMeta.Name) + case dataselect.CreationTimestampProperty: + return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time) + case dataselect.NamespaceProperty: + return dataselect.StdComparableString(self.ObjectMeta.Namespace) + default: + // if name is not supported then just return a constant dummy value, sort will have no effect. + return nil + } +} + +func toCells(std []apps.StatefulSet) []dataselect.DataCell { + cells := make([]dataselect.DataCell, len(std)) + for i := range std { + cells[i] = StatefulSetCell(std[i]) + } + return cells +} + +func fromCells(cells []dataselect.DataCell) []apps.StatefulSet { + std := make([]apps.StatefulSet, len(cells)) + for i := range std { + std[i] = apps.StatefulSet(cells[i].(StatefulSetCell)) + } + return std +} + +func getStatus(list *apps.StatefulSetList, pods []v1.Pod, events []v1.Event) common.ResourceStatus { + info := common.ResourceStatus{} + if list == nil { + return info + } + + for _, ss := range list.Items { + matchingPods := common.FilterPodsByControllerRef(&ss, pods) + podInfo := common.GetPodInfo(ss.Status.Replicas, ss.Spec.Replicas, matchingPods) + warnings := event.GetPodsEventWarnings(events, matchingPods) + + if len(warnings) > 0 { + info.Failed++ + } else if podInfo.Pending > 0 { + info.Pending++ + } else { + info.Running++ + } + } + + return info +} diff --git a/pkg/resource/statefulset/detail.go b/pkg/resource/statefulset/detail.go new file mode 100644 index 00000000..ce0db6bb --- /dev/null +++ b/pkg/resource/statefulset/detail.go @@ -0,0 +1,47 @@ +package statefulset + +import ( + "context" + "github.com/karmada-io/dashboard/pkg/common/errors" + "github.com/karmada-io/dashboard/pkg/resource/common" + apps "k8s.io/api/apps/v1" + metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "log" +) + +// StatefulSetDetail is a presentation layer view of Kubernetes Stateful Set resource. This means it is Stateful +type StatefulSetDetail struct { + // Extends list item structure. + StatefulSet `json:",inline"` + + // List of non-critical errors, that occurred during resource retrieval. + Errors []error `json:"errors"` +} + +// GetStatefulSetDetail gets Stateful Set details. +func GetStatefulSetDetail(client kubernetes.Interface, namespace, + name string) (*StatefulSetDetail, error) { + log.Printf("Getting details of %s statefulset in %s namespace", name, namespace) + + ss, err := client.AppsV1().StatefulSets(namespace).Get(context.TODO(), name, metaV1.GetOptions{}) + if err != nil { + return nil, err + } + + podInfo, err := getStatefulSetPodInfo(client, ss) + nonCriticalErrors, criticalError := errors.ExtractErrors(err) + if criticalError != nil { + return nil, criticalError + } + + ssDetail := getStatefulSetDetail(ss, podInfo, nonCriticalErrors) + return &ssDetail, nil +} + +func getStatefulSetDetail(statefulSet *apps.StatefulSet, podInfo *common.PodInfo, nonCriticalErrors []error) StatefulSetDetail { + return StatefulSetDetail{ + StatefulSet: toStatefulSet(statefulSet, podInfo), + Errors: nonCriticalErrors, + } +} diff --git a/pkg/resource/statefulset/list.go b/pkg/resource/statefulset/list.go new file mode 100644 index 00000000..9deb5ee1 --- /dev/null +++ b/pkg/resource/statefulset/list.go @@ -0,0 +1,110 @@ +package statefulset + +import ( + "github.com/karmada-io/dashboard/pkg/common/errors" + "github.com/karmada-io/dashboard/pkg/common/types" + "github.com/karmada-io/dashboard/pkg/dataselect" + "github.com/karmada-io/dashboard/pkg/resource/common" + "github.com/karmada-io/dashboard/pkg/resource/event" + apps "k8s.io/api/apps/v1" + v1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes" + "log" +) + +// StatefulSetList contains a list of Stateful Sets in the cluster. +type StatefulSetList struct { + ListMeta types.ListMeta `json:"listMeta"` + + Status common.ResourceStatus `json:"status"` + StatefulSets []StatefulSet `json:"statefulSets"` + + // List of non-critical errors, that occurred during resource retrieval. + Errors []error `json:"errors"` +} + +// StatefulSet is a presentation layer view of Kubernetes Stateful Set resource. +type StatefulSet struct { + ObjectMeta types.ObjectMeta `json:"objectMeta"` + TypeMeta types.TypeMeta `json:"typeMeta"` + Pods common.PodInfo `json:"podInfo"` + ContainerImages []string `json:"containerImages"` + InitContainerImages []string `json:"initContainerImages"` +} + +// GetStatefulSetList returns a list of all Stateful Sets in the cluster. +func GetStatefulSetList(client kubernetes.Interface, nsQuery *common.NamespaceQuery, + dsQuery *dataselect.DataSelectQuery) (*StatefulSetList, error) { + log.Print("Getting list of all stateful sets in the cluster") + + channels := &common.ResourceChannels{ + StatefulSetList: common.GetStatefulSetListChannel(client, nsQuery, 1), + PodList: common.GetPodListChannel(client, nsQuery, 1), + EventList: common.GetEventListChannel(client, nsQuery, 1), + } + + return GetStatefulSetListFromChannels(channels, dsQuery) +} + +// GetStatefulSetListFromChannels returns a list of all Stateful Sets in the cluster reading +// required resource list once from the channels. +func GetStatefulSetListFromChannels(channels *common.ResourceChannels, dsQuery *dataselect.DataSelectQuery) (*StatefulSetList, error) { + + statefulSets := <-channels.StatefulSetList.List + err := <-channels.StatefulSetList.Error + nonCriticalErrors, criticalError := errors.ExtractErrors(err) + if criticalError != nil { + return nil, criticalError + } + + pods := <-channels.PodList.List + err = <-channels.PodList.Error + nonCriticalErrors, criticalError = errors.AppendError(err, nonCriticalErrors) + if criticalError != nil { + return nil, criticalError + } + + events := <-channels.EventList.List + err = <-channels.EventList.Error + nonCriticalErrors, criticalError = errors.AppendError(err, nonCriticalErrors) + if criticalError != nil { + return nil, criticalError + } + + ssList := toStatefulSetList(statefulSets.Items, pods.Items, events.Items, nonCriticalErrors, dsQuery) + ssList.Status = getStatus(statefulSets, pods.Items, events.Items) + return ssList, nil +} + +func toStatefulSetList(statefulSets []apps.StatefulSet, pods []v1.Pod, events []v1.Event, nonCriticalErrors []error, + dsQuery *dataselect.DataSelectQuery) *StatefulSetList { + + statefulSetList := &StatefulSetList{ + StatefulSets: make([]StatefulSet, 0), + ListMeta: types.ListMeta{TotalItems: len(statefulSets)}, + Errors: nonCriticalErrors, + } + + ssCells, filteredTotal := dataselect.GenericDataSelectWithFilter(toCells(statefulSets), dsQuery) + statefulSets = fromCells(ssCells) + statefulSetList.ListMeta = types.ListMeta{TotalItems: filteredTotal} + + for _, statefulSet := range statefulSets { + matchingPods := common.FilterPodsByControllerRef(&statefulSet, pods) + podInfo := common.GetPodInfo(statefulSet.Status.Replicas, statefulSet.Spec.Replicas, matchingPods) + podInfo.Warnings = event.GetPodsEventWarnings(events, matchingPods) + statefulSetList.StatefulSets = append(statefulSetList.StatefulSets, toStatefulSet(&statefulSet, &podInfo)) + } + + return statefulSetList +} + +func toStatefulSet(statefulSet *apps.StatefulSet, podInfo *common.PodInfo) StatefulSet { + return StatefulSet{ + ObjectMeta: types.NewObjectMeta(statefulSet.ObjectMeta), + TypeMeta: types.NewTypeMeta(types.ResourceKindStatefulSet), + ContainerImages: common.GetContainerImages(&statefulSet.Spec.Template.Spec), + InitContainerImages: common.GetInitContainerImages(&statefulSet.Spec.Template.Spec), + Pods: *podInfo, + } +} diff --git a/pkg/resource/statefulset/pods.go b/pkg/resource/statefulset/pods.go new file mode 100644 index 00000000..88d67a33 --- /dev/null +++ b/pkg/resource/statefulset/pods.go @@ -0,0 +1,40 @@ +package statefulset + +import ( + "context" + "github.com/karmada-io/dashboard/pkg/resource/common" + apps "k8s.io/api/apps/v1" + v1 "k8s.io/api/core/v1" + metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +// getRawStatefulSetPods return array of api pods targeting pet set with given name. +func getRawStatefulSetPods(client kubernetes.Interface, name, namespace string) ([]v1.Pod, error) { + statefulSet, err := client.AppsV1().StatefulSets(namespace).Get(context.TODO(), name, metaV1.GetOptions{}) + if err != nil { + return nil, err + } + + channels := &common.ResourceChannels{ + PodList: common.GetPodListChannel(client, common.NewSameNamespaceQuery(namespace), 1), + } + + podList := <-channels.PodList.List + if err := <-channels.PodList.Error; err != nil { + return nil, err + } + + return common.FilterPodsByControllerRef(statefulSet, podList.Items), nil +} + +// Returns simple info about pods(running, desired, failing, etc.) related to given pet set. +func getStatefulSetPodInfo(client kubernetes.Interface, statefulSet *apps.StatefulSet) (*common.PodInfo, error) { + pods, err := getRawStatefulSetPods(client, statefulSet.Name, statefulSet.Namespace) + if err != nil { + return nil, err + } + + podInfo := common.GetPodInfo(statefulSet.Status.Replicas, statefulSet.Spec.Replicas, pods) + return &podInfo, nil +} diff --git a/ui/apps/dashboard/src/components/tag-list/index.tsx b/ui/apps/dashboard/src/components/tag-list/index.tsx index 318e0f29..e7d0535b 100644 --- a/ui/apps/dashboard/src/components/tag-list/index.tsx +++ b/ui/apps/dashboard/src/components/tag-list/index.tsx @@ -1,5 +1,6 @@ import { FC } from 'react'; import { Dropdown, Tag } from 'antd'; +import { cn } from '@/utils/cn.ts'; interface ITagListProps { tags: { @@ -18,12 +19,14 @@ const TagList: FC = (props) => { ) : tags.length <= maxLen ? ( tags.map((t) => {t.value}) ) : ( -
-
- {tags.slice(0, maxLen).map((t) => ( - {t.value} - ))} -
+
maxLen, + })} + > + {tags.slice(0, maxLen).map((t) => ( + {t.value} + ))} ({ diff --git a/ui/apps/dashboard/src/pages/multicloud-resource-manage/workload/index.tsx b/ui/apps/dashboard/src/pages/multicloud-resource-manage/workload/index.tsx index 07b6bbbf..41cc22d0 100644 --- a/ui/apps/dashboard/src/pages/multicloud-resource-manage/workload/index.tsx +++ b/ui/apps/dashboard/src/pages/multicloud-resource-manage/workload/index.tsx @@ -5,6 +5,7 @@ import { Input, message, Popconfirm, + Segmented, Select, Space, Table, @@ -13,8 +14,8 @@ import { } from 'antd'; import { Icons } from '@/components/icons'; import { GetNamespaces } from '@/services/namespace'; -import { GetWorkloads } from '@/services/workload'; import type { DeploymentWorkload } from '@/services/workload'; +import { GetWorkloads } from '@/services/workload'; import { useQuery } from '@tanstack/react-query'; import { useCallback, useMemo, useState } from 'react'; import { DeleteResource, GetResource } from '@/services/unstructured.ts'; @@ -22,16 +23,28 @@ import NewWorkloadEditorModal from './new-workload-editor-modal.tsx'; import WorkloadDetailDrawer, { WorkloadDetailDrawerProps, } from './workload-detail-drawer.tsx'; -import { useToggle } from '@uidotdev/usehooks'; +import { useToggle, useWindowSize } from '@uidotdev/usehooks'; import { stringify } from 'yaml'; +import TagList from '@/components/tag-list'; +import { WorkloadKind } from '@/services/base.ts'; /* propagationpolicy.karmada.io/name: "nginx-propagation" propagationpolicy.karmada.io/namespace: "default" */ + const propagationpolicyKey = 'propagationpolicy.karmada.io/name'; const WorkloadPage = () => { - const { data: nsData } = useQuery({ + const [filter, setFilter] = useState<{ + kind: WorkloadKind; + selectedWorkSpace: string; + searchText: string; + }>({ + kind: WorkloadKind.Deployment, + selectedWorkSpace: '', + searchText: '', + }); + const { data: nsData, isLoading: isNsDataLoading } = useQuery({ queryKey: ['GetNamespaces'], queryFn: async () => { const clusters = await GetNamespaces({}); @@ -48,9 +61,13 @@ const WorkloadPage = () => { }); }, [nsData]); const { data, isLoading, refetch } = useQuery({ - queryKey: ['GetWorkloads'], + queryKey: ['GetWorkloads', JSON.stringify(filter)], queryFn: async () => { - const clusters = await GetWorkloads({}); + const clusters = await GetWorkloads({ + kind: filter.kind, + namespace: filter.selectedWorkSpace, + keyword: filter.searchText, + }); return clusters.data || {}; }, }); @@ -58,7 +75,7 @@ const WorkloadPage = () => { Omit >({ open: false, - kind: '', + kind: WorkloadKind.Unknown, namespace: '', name: '', }); @@ -76,6 +93,7 @@ const WorkloadPage = () => { content: '', }); }, []); + const size = useWindowSize(); const columns: TableColumnProps[] = [ { title: i18nInstance.t('a4b28a416f0b6f3c215c51e79e517298'), @@ -102,14 +120,17 @@ const WorkloadPage = () => { if (!r?.objectMeta?.labels) { return '-'; } + const params = Object.keys(r.objectMeta.labels).map((key) => { + return { + key: `${r.objectMeta.name}-${key}`, + value: `${key}:${r.objectMeta.labels[key]}`, + }; + }); return ( -
- {Object.keys(r.objectMeta.labels).map((key) => ( - - {key}:{r.objectMeta.labels[key]} - - ))} -
+ 1800 ? undefined : 1} + /> ); }, }, @@ -144,7 +165,7 @@ const WorkloadPage = () => { onClick={() => { setDrawerData({ open: true, - kind: r.typeMeta.kind, + kind: r.typeMeta.kind as WorkloadKind, name: r.objectMeta.name, namespace: r.objectMeta.namespace, }); @@ -199,20 +220,43 @@ const WorkloadPage = () => { ]; const [messageApi, messageContextHolder] = message.useMessage(); + return (
-
-

- {i18nInstance.t('280c56077360c204e536eb770495bc5f')} -

- { + setFilter({ + ...filter, + selectedWorkSpace: v, + }); + }} + /> + { + const input = e.currentTarget.value; + setFilter({ + ...filter, + searchText: input, + }); + }} + /> +
r.objectMeta.name || ''} columns={columns} loading={isLoading} - dataSource={data?.deployments || []} + dataSource={data ? data.deployments || data.statefulSets : []} /> { onClose={() => { setDrawerData({ open: false, - kind: '', + kind: WorkloadKind.Unknown, namespace: '', name: '', }); diff --git a/ui/apps/dashboard/src/pages/multicloud-resource-manage/workload/workload-detail-drawer.tsx b/ui/apps/dashboard/src/pages/multicloud-resource-manage/workload/workload-detail-drawer.tsx index 293a5faf..fefb01ef 100644 --- a/ui/apps/dashboard/src/pages/multicloud-resource-manage/workload/workload-detail-drawer.tsx +++ b/ui/apps/dashboard/src/pages/multicloud-resource-manage/workload/workload-detail-drawer.tsx @@ -17,10 +17,11 @@ import { import { useQuery } from '@tanstack/react-query'; import dayjs from 'dayjs'; import styles from './index.module.less'; +import { WorkloadKind } from '@/services/base.ts'; export interface WorkloadDetailDrawerProps { open: boolean; - kind: string; + kind: WorkloadKind; namespace: string; name: string; onClose: () => void; @@ -32,22 +33,24 @@ const WorkloadDetailDrawer: FC = (props) => { return !!(kind && name && namespace); }, [kind, name, namespace]); const { data: detailData, isLoading: isDetailDataLoading } = useQuery({ - queryKey: ['GetWorkloadDetail', kind, name, name], + queryKey: ['GetWorkloadDetail', kind, name, namespace], queryFn: async () => { const workloadDetailRet = await GetWorkloadDetail({ namespace, name, + kind, }); return workloadDetailRet.data || {}; }, enabled: enableFetch, }); const { data: eventsData } = useQuery({ - queryKey: ['GetWorkloadEvents', kind, name, name], + queryKey: ['GetWorkloadEvents', kind, name, namespace], queryFn: async () => { const workloadEventsRet = await GetWorkloadEvents({ namespace, name, + kind, }); return workloadEventsRet.data || {}; }, diff --git a/ui/apps/dashboard/src/services/base.ts b/ui/apps/dashboard/src/services/base.ts index 4939a9b6..75596429 100644 --- a/ui/apps/dashboard/src/services/base.ts +++ b/ui/apps/dashboard/src/services/base.ts @@ -55,3 +55,9 @@ export interface RollingUpdateStrategy { maxSurge: string; maxUnavailable: string; } + +export enum WorkloadKind { + Unknown = '', + Deployment = 'deployment', + Statefulset = 'statefulset', +} diff --git a/ui/apps/dashboard/src/services/workload.ts b/ui/apps/dashboard/src/services/workload.ts index f46d8c3e..a07e0046 100644 --- a/ui/apps/dashboard/src/services/workload.ts +++ b/ui/apps/dashboard/src/services/workload.ts @@ -1,8 +1,11 @@ import { + convertDataSelectQuery, + DataSelectQuery, IResponse, karmadaClient, RollingUpdateStrategy, Selector, + WorkloadKind, } from '@/services/base.ts'; import { ObjectMeta, TypeMeta } from '@/services/base'; @@ -14,6 +17,15 @@ export interface DeploymentWorkload { initContainerImages: any; } +export interface StatefulsetWorkload { + objectMeta: ObjectMeta; + typeMeta: TypeMeta; + pods: Pods; + containerImages: string[]; + initContainerImages: any; +} +export type Workload = DeploymentWorkload | StatefulsetWorkload; + export interface Pods { current: number; desired: number; @@ -32,9 +44,17 @@ export interface WorkloadStatus { terminating: number; } -export async function GetWorkloads(params: { namespace?: string }) { - const { namespace } = params; - const url = namespace ? `/deployment/${namespace}` : '/deployment'; +export async function GetWorkloads(params: { + namespace?: string; + kind: WorkloadKind; + keyword?: string; +}) { + const { kind, namespace } = params; + const requestData = {} as DataSelectQuery; + if (params.keyword) { + requestData.filterBy = ['name', params.keyword]; + } + const url = namespace ? `/${kind}/${namespace}` : `/${kind}`; const resp = await karmadaClient.get< IResponse<{ errors: string[]; @@ -42,9 +62,12 @@ export async function GetWorkloads(params: { namespace?: string }) { totalItems: number; }; status: WorkloadStatus; - deployments: DeploymentWorkload[]; + deployments?: Workload[]; + statefulSets?: Workload[]; }> - >(url); + >(url, { + params: convertDataSelectQuery(requestData), + }); return resp.data; } @@ -73,16 +96,18 @@ export interface WorkloadStatusInfo { export async function GetWorkloadDetail(params: { namespace?: string; name: string; + kind: WorkloadKind; }) { // /deployment/:namespace/:deployment - const { name, namespace } = params; + const { kind, name, namespace } = params; + const url = `/${kind}/${namespace}/${name}`; const resp = await karmadaClient.get< IResponse< { errors: string[]; } & WorkloadDetail > - >(`/deployment/${namespace}/${name}`); + >(url); return resp.data; } @@ -106,8 +131,10 @@ export interface WorkloadEvent { export async function GetWorkloadEvents(params: { namespace: string; name: string; + kind: WorkloadKind; }) { - const { name, namespace } = params; + const { kind, name, namespace } = params; + const url = `/${kind}/${namespace}/${name}/event`; const resp = await karmadaClient.get< IResponse<{ errors: string[]; @@ -116,7 +143,7 @@ export async function GetWorkloadEvents(params: { }; events: WorkloadEvent[]; }> - >(`/deployment/${namespace}/${name}/event`); + >(url); return resp.data; }