-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implement statefulset resources (#65)
* feat: not allow wrap for small device Signed-off-by: warjiang <[email protected]> * feat: api for statefulset Signed-off-by: warjiang <[email protected]> * feat: opt label view for workloads Signed-off-by: warjiang <[email protected]> * feat: add search filter for workload; add statefulset implementation Signed-off-by: warjiang <[email protected]> --------- Signed-off-by: warjiang <[email protected]>
- Loading branch information
Showing
12 changed files
with
501 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.