diff --git a/cmd/api/app/api.go b/cmd/api/app/api.go index 1213b606..063cabd0 100644 --- a/cmd/api/app/api.go +++ b/cmd/api/app/api.go @@ -17,6 +17,7 @@ import ( // Importing route packages forces route registration _ "github.com/karmada-io/dashboard/cmd/api/app/routes/auth" _ "github.com/karmada-io/dashboard/cmd/api/app/routes/cluster" + _ "github.com/karmada-io/dashboard/cmd/api/app/routes/clusterpropagationpolicy" _ "github.com/karmada-io/dashboard/cmd/api/app/routes/config" _ "github.com/karmada-io/dashboard/cmd/api/app/routes/configmap" _ "github.com/karmada-io/dashboard/cmd/api/app/routes/cronjob" diff --git a/cmd/api/app/routes/clusterpropagationpolicy/handler.go b/cmd/api/app/routes/clusterpropagationpolicy/handler.go new file mode 100644 index 00000000..4c102319 --- /dev/null +++ b/cmd/api/app/routes/clusterpropagationpolicy/handler.go @@ -0,0 +1,161 @@ +package propagationpolicy + +import ( + "context" + "github.com/gin-gonic/gin" + "github.com/karmada-io/dashboard/cmd/api/app/router" + v1 "github.com/karmada-io/dashboard/cmd/api/app/types/api/v1" + "github.com/karmada-io/dashboard/cmd/api/app/types/common" + "github.com/karmada-io/dashboard/pkg/client" + "github.com/karmada-io/dashboard/pkg/common/errors" + "github.com/karmada-io/dashboard/pkg/resource/clusterpropagationpolicy" + "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/util/retry" + "k8s.io/klog/v2" + "sigs.k8s.io/yaml" +) + +func handleGetClusterPropagationPolicyList(c *gin.Context) { + karmadaClient := client.InClusterKarmadaClient() + dataSelect := common.ParseDataSelectPathParameter(c) + clusterPropagationList, err := clusterpropagationpolicy.GetClusterPropagationPolicyList(karmadaClient, dataSelect) + if err != nil { + klog.ErrorS(err, "Failed to GetClusterPropagationPolicyList") + common.Fail(c, err) + return + } + common.Success(c, clusterPropagationList) +} + +func handleGetClusterPropagationPolicyDetail(c *gin.Context) { + karmadaClient := client.InClusterKarmadaClient() + name := c.Param("clusterPropagationPolicyName") + result, err := clusterpropagationpolicy.GetClusterPropagationPolicyDetail(karmadaClient, name) + if err != nil { + klog.ErrorS(err, "GetClusterPropagationPolicyDetail failed") + common.Fail(c, err) + return + } + common.Success(c, result) +} + +func handlePostClusterPropagationPolicy(c *gin.Context) { + ctx := context.Context(c) + propagationpolicyRequest := new(v1.PostPropagationPolicyRequest) + if err := c.ShouldBind(&propagationpolicyRequest); err != nil { + common.Fail(c, err) + return + } + + var err error + karmadaClient := client.InClusterKarmadaClient() + if propagationpolicyRequest.IsClusterScope { + clusterPropagationPolicy := v1alpha1.ClusterPropagationPolicy{} + if err = yaml.Unmarshal([]byte(propagationpolicyRequest.PropagationData), &clusterPropagationPolicy); err != nil { + klog.ErrorS(err, "Failed to unmarshal ClusterPropagationPolicy") + common.Fail(c, err) + return + } + _, err = karmadaClient.PolicyV1alpha1().ClusterPropagationPolicies().Create(ctx, &clusterPropagationPolicy, metav1.CreateOptions{}) + } else { + propagationPolicy := v1alpha1.PropagationPolicy{} + if err = yaml.Unmarshal([]byte(propagationpolicyRequest.PropagationData), &propagationPolicy); err != nil { + klog.ErrorS(err, "Failed to unmarshal PropagationPolicy") + common.Fail(c, err) + return + } + _, err = karmadaClient.PolicyV1alpha1().PropagationPolicies(propagationpolicyRequest.Namespace).Create(ctx, &propagationPolicy, metav1.CreateOptions{}) + } + if err != nil { + klog.ErrorS(err, "Failed to create PropagationPolicy") + common.Fail(c, err) + return + } + common.Success(c, "ok") +} + +func handlePutClusterPropagationPolicy(c *gin.Context) { + ctx := context.Context(c) + propagationpolicyRequest := new(v1.PutPropagationPolicyRequest) + if err := c.ShouldBind(&propagationpolicyRequest); err != nil { + common.Fail(c, err) + return + } + var err error + karmadaClient := client.InClusterKarmadaClient() + // todo check pp exist + if propagationpolicyRequest.IsClusterScope { + clusterPropagationPolicy := v1alpha1.ClusterPropagationPolicy{} + if err = yaml.Unmarshal([]byte(propagationpolicyRequest.PropagationData), &clusterPropagationPolicy); err != nil { + klog.ErrorS(err, "Failed to unmarshal ClusterPropagationPolicy") + common.Fail(c, err) + return + } + _, err = karmadaClient.PolicyV1alpha1().ClusterPropagationPolicies().Update(ctx, &clusterPropagationPolicy, metav1.UpdateOptions{}) + } else { + propagationPolicy := v1alpha1.PropagationPolicy{} + if err = yaml.Unmarshal([]byte(propagationpolicyRequest.PropagationData), &propagationPolicy); err != nil { + klog.ErrorS(err, "Failed to unmarshal PropagationPolicy") + common.Fail(c, err) + return + } + var oldPropagationPolicy *v1alpha1.PropagationPolicy + oldPropagationPolicy, err = karmadaClient.PolicyV1alpha1().PropagationPolicies(propagationpolicyRequest.Namespace).Get(ctx, propagationpolicyRequest.Name, metav1.GetOptions{}) + if err == nil { + // only spec can be updated + propagationPolicy.TypeMeta = oldPropagationPolicy.TypeMeta + propagationPolicy.ObjectMeta = oldPropagationPolicy.ObjectMeta + _, err = karmadaClient.PolicyV1alpha1().PropagationPolicies(propagationpolicyRequest.Namespace).Update(ctx, &propagationPolicy, metav1.UpdateOptions{}) + } + } + if err != nil { + klog.ErrorS(err, "Failed to update PropagationPolicy") + common.Fail(c, err) + return + } + common.Success(c, "ok") +} +func handleDeleteClusterPropagationPolicy(c *gin.Context) { + ctx := context.Context(c) + propagationpolicyRequest := new(v1.DeletePropagationPolicyRequest) + if err := c.ShouldBind(&propagationpolicyRequest); err != nil { + common.Fail(c, err) + return + } + var err error + karmadaClient := client.InClusterKarmadaClient() + if propagationpolicyRequest.IsClusterScope { + err = karmadaClient.PolicyV1alpha1().ClusterPropagationPolicies().Delete(ctx, propagationpolicyRequest.Name, metav1.DeleteOptions{}) + if err != nil { + klog.ErrorS(err, "Failed to delete PropagationPolicy") + common.Fail(c, err) + return + } + } else { + err = karmadaClient.PolicyV1alpha1().PropagationPolicies(propagationpolicyRequest.Namespace).Delete(ctx, propagationpolicyRequest.Name, metav1.DeleteOptions{}) + if err != nil { + klog.ErrorS(err, "Failed to delete PropagationPolicy") + common.Fail(c, err) + return + } + err = retry.OnError( + retry.DefaultRetry, + func(err error) bool { + return errors.IsNotFound(err) + }, + func() error { + _, getErr := karmadaClient.PolicyV1alpha1().PropagationPolicies(propagationpolicyRequest.Namespace).Get(ctx, propagationpolicyRequest.Name, metav1.GetOptions{}) + return getErr + }) + } + + common.Success(c, "ok") +} + +func init() { + r := router.V1() + r.GET("/clusterpropagationpolicy", handleGetClusterPropagationPolicyList) + r.GET("/clusterpropagationpolicy/:clusterPropagationPolicyName", handleGetClusterPropagationPolicyDetail) + r.POST("/clusterpropagationpolicy", handlePostClusterPropagationPolicy) +} diff --git a/pkg/common/types/resourcekind.go b/pkg/common/types/resourcekind.go index 9101e377..615f617a 100644 --- a/pkg/common/types/resourcekind.go +++ b/pkg/common/types/resourcekind.go @@ -9,6 +9,7 @@ type ResourceKind string const ( ResourceKindCluster = "cluster" ResourceKindPropagationPolicy = "propagationpolicy" + ResourceKindClusterPropagationPolicy = "clusterpropagationpolicy" ResourceKindConfigMap = "configmap" ResourceKindDaemonSet = "daemonset" ResourceKindDeployment = "deployment" diff --git a/pkg/resource/clusterpropagationpolicy/common.go b/pkg/resource/clusterpropagationpolicy/common.go new file mode 100644 index 00000000..31de74dd --- /dev/null +++ b/pkg/resource/clusterpropagationpolicy/common.go @@ -0,0 +1,36 @@ +package clusterpropagationpolicy + +import ( + "github.com/karmada-io/dashboard/pkg/dataselect" + "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1" +) + +type ClusterPropagationPolicyCell v1alpha1.ClusterPropagationPolicy + +func (self ClusterPropagationPolicyCell) 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) + default: + // if name is not supported then just return a constant dummy value, sort will have no effect. + return nil + } +} + +func toCells(std []v1alpha1.ClusterPropagationPolicy) []dataselect.DataCell { + cells := make([]dataselect.DataCell, len(std)) + for i := range std { + cells[i] = ClusterPropagationPolicyCell(std[i]) + } + return cells +} + +func fromCells(cells []dataselect.DataCell) []v1alpha1.ClusterPropagationPolicy { + std := make([]v1alpha1.ClusterPropagationPolicy, len(cells)) + for i := range std { + std[i] = v1alpha1.ClusterPropagationPolicy(cells[i].(ClusterPropagationPolicyCell)) + } + return std +} diff --git a/pkg/resource/clusterpropagationpolicy/detail.go b/pkg/resource/clusterpropagationpolicy/detail.go new file mode 100644 index 00000000..0d904422 --- /dev/null +++ b/pkg/resource/clusterpropagationpolicy/detail.go @@ -0,0 +1,40 @@ +package clusterpropagationpolicy + +import ( + "context" + "github.com/karmada-io/dashboard/pkg/common/errors" + "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1" + karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned" + metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type ClusterPropagationPolicyDetail struct { + // Extends list item structure. + ClusterPropagationPolicy `json:",inline"` + + // List of non-critical errors, that occurred during resource retrieval. + Errors []error `json:"errors"` +} + +// GetClusterPropagationPolicyDetail gets clusterPropagationPolicy details. +func GetClusterPropagationPolicyDetail(client karmadaclientset.Interface, name string) (*ClusterPropagationPolicyDetail, error) { + propagationpolicyData, err := client.PolicyV1alpha1().ClusterPropagationPolicies().Get(context.TODO(), name, metaV1.GetOptions{}) + if err != nil { + return nil, err + } + + nonCriticalErrors, criticalError := errors.ExtractErrors(err) + if criticalError != nil { + return nil, criticalError + } + + propagationpolicy := toPropagationPolicyDetail(propagationpolicyData, nonCriticalErrors) + return &propagationpolicy, nil +} + +func toPropagationPolicyDetail(clusterPropagationpolicy *v1alpha1.ClusterPropagationPolicy, nonCriticalErrors []error) ClusterPropagationPolicyDetail { + return ClusterPropagationPolicyDetail{ + ClusterPropagationPolicy: toClusterPropagationPolicy(clusterPropagationpolicy), + Errors: nonCriticalErrors, + } +} diff --git a/pkg/resource/clusterpropagationpolicy/list.go b/pkg/resource/clusterpropagationpolicy/list.go new file mode 100644 index 00000000..432bf974 --- /dev/null +++ b/pkg/resource/clusterpropagationpolicy/list.go @@ -0,0 +1,67 @@ +package clusterpropagationpolicy + +import ( + "context" + "github.com/karmada-io/dashboard/pkg/common/errors" + "github.com/karmada-io/dashboard/pkg/common/helpers" + "github.com/karmada-io/dashboard/pkg/common/types" + "github.com/karmada-io/dashboard/pkg/dataselect" + "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1" + karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned" +) + +// ClusterPropagationPolicyList contains a list of propagation in the karmada control-plane. +type ClusterPropagationPolicyList struct { + ListMeta types.ListMeta `json:"listMeta"` + + // Unordered list of PropagationPolicies. + ClusterPropagationPolicies []ClusterPropagationPolicy `json:"clusterPropagationPolicies"` + + // List of non-critical errors, that occurred during resource retrieval. + Errors []error `json:"errors"` +} + +type ClusterPropagationPolicy struct { + ObjectMeta types.ObjectMeta `json:"objectMeta"` + TypeMeta types.TypeMeta `json:"typeMeta"` + SchedulerName string `json:"schedulerName"` + ClusterAffinity *v1alpha1.ClusterAffinity `json:"clusterAffinity"` + ResourceSelectors []v1alpha1.ResourceSelector `json:"resourceSelectors"` +} + +// GetClusterPropagationPolicyList returns a list of all propagations in the karmada control-plance. +func GetClusterPropagationPolicyList(client karmadaclientset.Interface, dsQuery *dataselect.DataSelectQuery) (*ClusterPropagationPolicyList, error) { + clusterPropagationPolicies, err := client.PolicyV1alpha1().ClusterPropagationPolicies().List(context.TODO(), helpers.ListEverything) + nonCriticalErrors, criticalError := errors.ExtractErrors(err) + if criticalError != nil { + return nil, criticalError + } + + return toClusterPropagationPolicyList(clusterPropagationPolicies.Items, nonCriticalErrors, dsQuery), nil +} + +func toClusterPropagationPolicyList(clusterPropagationPolicies []v1alpha1.ClusterPropagationPolicy, nonCriticalErrors []error, dsQuery *dataselect.DataSelectQuery) *ClusterPropagationPolicyList { + propagationpolicyList := &ClusterPropagationPolicyList{ + ClusterPropagationPolicies: make([]ClusterPropagationPolicy, 0), + ListMeta: types.ListMeta{TotalItems: len(clusterPropagationPolicies)}, + } + clusterPropagationPolicyCells, filteredTotal := dataselect.GenericDataSelectWithFilter(toCells(clusterPropagationPolicies), dsQuery) + clusterPropagationPolicies = fromCells(clusterPropagationPolicyCells) + propagationpolicyList.ListMeta = types.ListMeta{TotalItems: filteredTotal} + propagationpolicyList.Errors = nonCriticalErrors + + for _, clusterPropagationPolicy := range clusterPropagationPolicies { + clusterPP := toClusterPropagationPolicy(&clusterPropagationPolicy) + propagationpolicyList.ClusterPropagationPolicies = append(propagationpolicyList.ClusterPropagationPolicies, clusterPP) + } + return propagationpolicyList +} + +func toClusterPropagationPolicy(propagationpolicy *v1alpha1.ClusterPropagationPolicy) ClusterPropagationPolicy { + return ClusterPropagationPolicy{ + ObjectMeta: types.NewObjectMeta(propagationpolicy.ObjectMeta), + TypeMeta: types.NewTypeMeta(types.ResourceKindClusterPropagationPolicy), + SchedulerName: propagationpolicy.Spec.SchedulerName, + ClusterAffinity: propagationpolicy.Spec.Placement.ClusterAffinity, + } +}