diff --git a/autoscaling/v1alpha1/cronfederatedhpa_types.go b/autoscaling/v1alpha1/cronfederatedhpa_types.go new file mode 100644 index 0000000..36dc08a --- /dev/null +++ b/autoscaling/v1alpha1/cronfederatedhpa_types.go @@ -0,0 +1,218 @@ +/* +Copyright 2023 The Karmada Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + autoscalingv2 "k8s.io/api/autoscaling/v2" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:subresource:status +// +kubebuilder:resource:shortName=cronfhpa,categories={karmada-io} +// +kubebuilder:printcolumn:JSONPath=`.spec.scaleTargetRef.kind`,name=`REFERENCE-KIND`,type=string +// +kubebuilder:printcolumn:JSONPath=`.spec.scaleTargetRef.name`,name=`REFERENCE-NAME`,type=string +// +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name=`AGE`,type=date + +// CronFederatedHPA represents a collection of repeating schedule to scale +// replica number of a specific workload. It can scale any resource implementing +// the scale subresource as well as FederatedHPA. +type CronFederatedHPA struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec is the specification of the CronFederatedHPA. + // +required + Spec CronFederatedHPASpec `json:"spec"` + + // Status is the current status of the CronFederatedHPA. + // +optional + Status CronFederatedHPAStatus `json:"status"` +} + +// CronFederatedHPASpec is the specification of the CronFederatedHPA. +type CronFederatedHPASpec struct { + // ScaleTargetRef points to the target resource to scale. + // Target resource could be any resource that implementing the scale + // subresource like Deployment, or FederatedHPA. + // +required + ScaleTargetRef autoscalingv2.CrossVersionObjectReference `json:"scaleTargetRef"` + + // Rules contains a collection of schedules that declares when and how + // the referencing target resource should be scaled. + // +required + Rules []CronFederatedHPARule `json:"rules"` +} + +// CronFederatedHPARule declares a schedule as well as scale actions. +type CronFederatedHPARule struct { + // Name of the rule. + // Each rule in a CronFederatedHPA must have a unique name. + // + // Note: the name will be used as an identifier to record its execution + // history. Changing the name will be considered as deleting the old rule + // and adding a new rule, that means the original execution history will be + // discarded. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=32 + // +required + Name string `json:"name"` + + // Schedule is the cron expression that represents a periodical time. + // The syntax follows https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#schedule-syntax. + // +required + Schedule string `json:"schedule"` + + // TargetReplicas is the target replicas to be scaled for resources + // referencing by ScaleTargetRef of this CronFederatedHPA. + // Only needed when referencing resource is not FederatedHPA. + // +optional + TargetReplicas *int32 `json:"targetReplicas,omitempty"` + + // TargetMinReplicas is the target MinReplicas to be set for FederatedHPA. + // Only needed when referencing resource is FederatedHPA. + // TargetMinReplicas and TargetMaxReplicas can be specified together or + // either one can be specified alone. + // nil means the MinReplicas(.spec.minReplicas) of the referencing FederatedHPA + // will not be updated. + // +optional + TargetMinReplicas *int32 `json:"targetMinReplicas,omitempty"` + + // TargetMaxReplicas is the target MaxReplicas to be set for FederatedHPA. + // Only needed when referencing resource is FederatedHPA. + // TargetMinReplicas and TargetMaxReplicas can be specified together or + // either one can be specified alone. + // nil means the MaxReplicas(.spec.maxReplicas) of the referencing FederatedHPA + // will not be updated. + // +optional + TargetMaxReplicas *int32 `json:"targetMaxReplicas,omitempty"` + + // Suspend tells the controller to suspend subsequent executions. + // Defaults to false. + // + // +kubebuilder:default=false + // +optional + Suspend *bool `json:"suspend,omitempty"` + + // TimeZone for the giving schedule. + // If not specified, this will default to the time zone of the + // karmada-controller-manager process. + // Invalid TimeZone will be rejected when applying by karmada-webhook. + // see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones for the + // all timezones. + // +optional + TimeZone *string `json:"timeZone,omitempty"` + + // SuccessfulHistoryLimit represents the count of successful execution items + // for each rule. + // The value must be a positive integer. It defaults to 3. + // + // +kubebuilder:default=3 + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=32 + // +optional + SuccessfulHistoryLimit *int32 `json:"successfulHistoryLimit,omitempty"` + + // FailedHistoryLimit represents the count of failed execution items for + // each rule. + // The value must be a positive integer. It defaults to 3. + // + // +kubebuilder:default=3 + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=32 + FailedHistoryLimit *int32 `json:"failedHistoryLimit,omitempty"` +} + +// CronFederatedHPAStatus represents the current status of a CronFederatedHPA. +type CronFederatedHPAStatus struct { + // ExecutionHistories record the execution histories of CronFederatedHPARule. + // +optional + ExecutionHistories []ExecutionHistory `json:"executionHistories,omitempty"` +} + +// ExecutionHistory records the execution history of specific CronFederatedHPARule. +type ExecutionHistory struct { + // RuleName is the name of the CronFederatedHPARule. + // +required + RuleName string `json:"ruleName"` + + // NextExecutionTime is the next time to execute. + // Nil means the rule has been suspended. + // +optional + NextExecutionTime *metav1.Time `json:"nextExecutionTime,omitempty"` + + // SuccessfulExecutions records successful executions. + // +optional + SuccessfulExecutions []SuccessfulExecution `json:"successfulExecutions,omitempty"` + + // FailedExecutions records failed executions. + // +optional + FailedExecutions []FailedExecution `json:"failedExecutions,omitempty"` +} + +// SuccessfulExecution records a successful execution. +type SuccessfulExecution struct { + // ScheduleTime is the expected execution time declared in CronFederatedHPARule. + // +required + ScheduleTime *metav1.Time `json:"scheduleTime"` + + // ExecutionTime is the actual execution time of CronFederatedHPARule. + // Tasks may not always be executed at ScheduleTime. ExecutionTime is used + // to evaluate the efficiency of the controller's execution. + // +required + ExecutionTime *metav1.Time `json:"executionTime"` + + // AppliedReplicas is the replicas have been applied. + // It is required if .spec.rules[*].targetReplicas is not empty. + // +optional + AppliedReplicas *int32 `json:"appliedReplicas,omitempty"` + + // AppliedMaxReplicas is the MaxReplicas have been applied. + // It is required if .spec.rules[*].targetMaxReplicas is not empty. + // +optional + AppliedMaxReplicas *int32 `json:"appliedMaxReplicas,omitempty"` + + // AppliedMinReplicas is the MinReplicas have been applied. + // It is required if .spec.rules[*].targetMinReplicas is not empty. + // +optional + AppliedMinReplicas *int32 `json:"appliedMinReplicas,omitempty"` +} + +// FailedExecution records a failed execution. +type FailedExecution struct { + // ScheduleTime is the expected execution time declared in CronFederatedHPARule. + // +required + ScheduleTime *metav1.Time `json:"scheduleTime"` + + // ExecutionTime is the actual execution time of CronFederatedHPARule. + // Tasks may not always be executed at ScheduleTime. ExecutionTime is used + // to evaluate the efficiency of the controller's execution. + // +required + ExecutionTime *metav1.Time `json:"executionTime"` + + // Message is the human-readable message indicating details about the failure. + // +required + Message string `json:"message"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// CronFederatedHPAList contains a list of CronFederatedHPA. +type CronFederatedHPAList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []CronFederatedHPA `json:"items"` +} diff --git a/autoscaling/v1alpha1/federatedhpa_types.go b/autoscaling/v1alpha1/federatedhpa_types.go index e54c970..b6358bd 100644 --- a/autoscaling/v1alpha1/federatedhpa_types.go +++ b/autoscaling/v1alpha1/federatedhpa_types.go @@ -9,6 +9,12 @@ import ( // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:subresource:status // +kubebuilder:resource:shortName=fhpa,categories={karmada-io} +// +kubebuilder:printcolumn:JSONPath=`.spec.scaleTargetRef.kind`,name=`REFERENCE-KIND`,type=string +// +kubebuilder:printcolumn:JSONPath=`.spec.scaleTargetRef.name`,name=`REFERENCE-NAME`,type=string +// +kubebuilder:printcolumn:JSONPath=`.spec.minReplicas`,name=`MINPODS`,type=integer +// +kubebuilder:printcolumn:JSONPath=`.spec.maxReplicas`,name=`MAXPODS`,type=integer +// +kubebuilder:printcolumn:JSONPath=`.status.currentReplicas`,name=`REPLICAS`,type=integer +// +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name=`AGE`,type=date // FederatedHPA is centralized HPA that can aggregate the metrics in multiple clusters. // When the system load increases, it will query the metrics from multiple clusters and scales up the replicas. @@ -18,7 +24,7 @@ type FederatedHPA struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - // Spec is the specification of the FederatedHPASpec. + // Spec is the specification of the FederatedHPA. // +required Spec FederatedHPASpec `json:"spec"` @@ -35,17 +41,16 @@ type FederatedHPASpec struct { // +required ScaleTargetRef autoscalingv2.CrossVersionObjectReference `json:"scaleTargetRef"` - // minReplicas is the lower limit for the number of replicas to which the autoscaler - // can scale down. It defaults to 1 pod. minReplicas is allowed to be 0 if the - // alpha feature gate HPAScaleToZero is enabled and at least one Object or External - // metric is configured. Scaling is active as long as at least one metric value is - // available. + // MinReplicas is the lower limit for the number of replicas to which the + // autoscaler can scale down. + // It defaults to 1 pod. // +optional - MinReplicas *int32 `json:"minReplicas,omitempty" protobuf:"varint,2,opt,name=minReplicas"` + MinReplicas *int32 `json:"minReplicas,omitempty"` - // maxReplicas is the upper limit for the number of replicas to which the autoscaler can scale up. + // MaxReplicas is the upper limit for the number of replicas to which the + // autoscaler can scale up. // It cannot be less that minReplicas. - MaxReplicas int32 `json:"maxReplicas" protobuf:"varint,3,opt,name=maxReplicas"` + MaxReplicas int32 `json:"maxReplicas"` // Metrics contains the specifications for which to use to calculate the // desired replica count (the maximum replica count across all metrics will diff --git a/autoscaling/v1alpha1/well_known_constants.go b/autoscaling/v1alpha1/well_known_constants.go new file mode 100644 index 0000000..8977d27 --- /dev/null +++ b/autoscaling/v1alpha1/well_known_constants.go @@ -0,0 +1,10 @@ +package v1alpha1 + +const ( + // FederatedHPAKind is the kind of FederatedHPA in group autoscaling.karmada.io + FederatedHPAKind = "FederatedHPA" + + // QuerySourceAnnotationKey is the annotation used in karmada-metrics-adapter to + // record the query source cluster + QuerySourceAnnotationKey = "resource.karmada.io/query-from-cluster" +) diff --git a/autoscaling/v1alpha1/zz_generated.deepcopy.go b/autoscaling/v1alpha1/zz_generated.deepcopy.go index be280ed..5c4f46b 100644 --- a/autoscaling/v1alpha1/zz_generated.deepcopy.go +++ b/autoscaling/v1alpha1/zz_generated.deepcopy.go @@ -10,6 +10,223 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CronFederatedHPA) DeepCopyInto(out *CronFederatedHPA) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CronFederatedHPA. +func (in *CronFederatedHPA) DeepCopy() *CronFederatedHPA { + if in == nil { + return nil + } + out := new(CronFederatedHPA) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CronFederatedHPA) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CronFederatedHPAList) DeepCopyInto(out *CronFederatedHPAList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CronFederatedHPA, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CronFederatedHPAList. +func (in *CronFederatedHPAList) DeepCopy() *CronFederatedHPAList { + if in == nil { + return nil + } + out := new(CronFederatedHPAList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CronFederatedHPAList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CronFederatedHPARule) DeepCopyInto(out *CronFederatedHPARule) { + *out = *in + if in.TargetReplicas != nil { + in, out := &in.TargetReplicas, &out.TargetReplicas + *out = new(int32) + **out = **in + } + if in.TargetMinReplicas != nil { + in, out := &in.TargetMinReplicas, &out.TargetMinReplicas + *out = new(int32) + **out = **in + } + if in.TargetMaxReplicas != nil { + in, out := &in.TargetMaxReplicas, &out.TargetMaxReplicas + *out = new(int32) + **out = **in + } + if in.Suspend != nil { + in, out := &in.Suspend, &out.Suspend + *out = new(bool) + **out = **in + } + if in.TimeZone != nil { + in, out := &in.TimeZone, &out.TimeZone + *out = new(string) + **out = **in + } + if in.SuccessfulHistoryLimit != nil { + in, out := &in.SuccessfulHistoryLimit, &out.SuccessfulHistoryLimit + *out = new(int32) + **out = **in + } + if in.FailedHistoryLimit != nil { + in, out := &in.FailedHistoryLimit, &out.FailedHistoryLimit + *out = new(int32) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CronFederatedHPARule. +func (in *CronFederatedHPARule) DeepCopy() *CronFederatedHPARule { + if in == nil { + return nil + } + out := new(CronFederatedHPARule) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CronFederatedHPASpec) DeepCopyInto(out *CronFederatedHPASpec) { + *out = *in + out.ScaleTargetRef = in.ScaleTargetRef + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]CronFederatedHPARule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CronFederatedHPASpec. +func (in *CronFederatedHPASpec) DeepCopy() *CronFederatedHPASpec { + if in == nil { + return nil + } + out := new(CronFederatedHPASpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CronFederatedHPAStatus) DeepCopyInto(out *CronFederatedHPAStatus) { + *out = *in + if in.ExecutionHistories != nil { + in, out := &in.ExecutionHistories, &out.ExecutionHistories + *out = make([]ExecutionHistory, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CronFederatedHPAStatus. +func (in *CronFederatedHPAStatus) DeepCopy() *CronFederatedHPAStatus { + if in == nil { + return nil + } + out := new(CronFederatedHPAStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExecutionHistory) DeepCopyInto(out *ExecutionHistory) { + *out = *in + if in.NextExecutionTime != nil { + in, out := &in.NextExecutionTime, &out.NextExecutionTime + *out = (*in).DeepCopy() + } + if in.SuccessfulExecutions != nil { + in, out := &in.SuccessfulExecutions, &out.SuccessfulExecutions + *out = make([]SuccessfulExecution, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.FailedExecutions != nil { + in, out := &in.FailedExecutions, &out.FailedExecutions + *out = make([]FailedExecution, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExecutionHistory. +func (in *ExecutionHistory) DeepCopy() *ExecutionHistory { + if in == nil { + return nil + } + out := new(ExecutionHistory) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FailedExecution) DeepCopyInto(out *FailedExecution) { + *out = *in + if in.ScheduleTime != nil { + in, out := &in.ScheduleTime, &out.ScheduleTime + *out = (*in).DeepCopy() + } + if in.ExecutionTime != nil { + in, out := &in.ExecutionTime, &out.ExecutionTime + *out = (*in).DeepCopy() + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FailedExecution. +func (in *FailedExecution) DeepCopy() *FailedExecution { + if in == nil { + return nil + } + out := new(FailedExecution) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FederatedHPA) DeepCopyInto(out *FederatedHPA) { *out = *in @@ -104,3 +321,42 @@ func (in *FederatedHPASpec) DeepCopy() *FederatedHPASpec { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SuccessfulExecution) DeepCopyInto(out *SuccessfulExecution) { + *out = *in + if in.ScheduleTime != nil { + in, out := &in.ScheduleTime, &out.ScheduleTime + *out = (*in).DeepCopy() + } + if in.ExecutionTime != nil { + in, out := &in.ExecutionTime, &out.ExecutionTime + *out = (*in).DeepCopy() + } + if in.AppliedReplicas != nil { + in, out := &in.AppliedReplicas, &out.AppliedReplicas + *out = new(int32) + **out = **in + } + if in.AppliedMaxReplicas != nil { + in, out := &in.AppliedMaxReplicas, &out.AppliedMaxReplicas + *out = new(int32) + **out = **in + } + if in.AppliedMinReplicas != nil { + in, out := &in.AppliedMinReplicas, &out.AppliedMinReplicas + *out = new(int32) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SuccessfulExecution. +func (in *SuccessfulExecution) DeepCopy() *SuccessfulExecution { + if in == nil { + return nil + } + out := new(SuccessfulExecution) + in.DeepCopyInto(out) + return out +} diff --git a/autoscaling/v1alpha1/zz_generated.register.go b/autoscaling/v1alpha1/zz_generated.register.go index c4baaab..5442863 100644 --- a/autoscaling/v1alpha1/zz_generated.register.go +++ b/autoscaling/v1alpha1/zz_generated.register.go @@ -42,6 +42,8 @@ func init() { // Adds the list of known types to Scheme. func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, + &CronFederatedHPA{}, + &CronFederatedHPAList{}, &FederatedHPA{}, &FederatedHPAList{}, ) diff --git a/cluster/mutation/mutation.go b/cluster/mutation/mutation.go index baefa3a..ceab5ef 100644 --- a/cluster/mutation/mutation.go +++ b/cluster/mutation/mutation.go @@ -18,11 +18,20 @@ const ( // MutateCluster mutates required fields of the Cluster. func MutateCluster(cluster *clusterapis.Cluster) { - MutateClusterTaints(cluster.Spec.Taints) + mutateClusterTaints(cluster.Spec.Taints) + migrateZoneToZones(cluster) } -// MutateClusterTaints add TimeAdded field for cluster NoExecute taints only if TimeAdded not set. -func MutateClusterTaints(taints []corev1.Taint) { +// migrateZoneToZones add zones field for cluster if Zones not set but Zone set only. +func migrateZoneToZones(cluster *clusterapis.Cluster) { + if cluster.Spec.Zone != "" && len(cluster.Spec.Zones) == 0 { + cluster.Spec.Zones = append(cluster.Spec.Zones, cluster.Spec.Zone) + cluster.Spec.Zone = "" + } +} + +// mutateClusterTaints add TimeAdded field for cluster NoExecute taints only if TimeAdded not set. +func mutateClusterTaints(taints []corev1.Taint) { for i := range taints { if taints[i].Effect == corev1.TaintEffectNoExecute && taints[i].TimeAdded == nil { now := metav1.Now() diff --git a/cluster/mutation/mutation_test.go b/cluster/mutation/mutation_test.go index ba35cc9..441a69f 100644 --- a/cluster/mutation/mutation_test.go +++ b/cluster/mutation/mutation_test.go @@ -1,6 +1,7 @@ package mutation import ( + "fmt" "math" "reflect" "testing" @@ -18,9 +19,10 @@ func TestMutateCluster(t *testing.T) { tests := []struct { name string args args + fun func(args) error }{ { - name: "test mutate cluster", + name: "test mutate cluster Taints", args: args{ cluster: &clusterapis.Cluster{ Spec: clusterapis.ClusterSpec{ @@ -33,20 +35,39 @@ func TestMutateCluster(t *testing.T) { { Key: "bar", Effect: corev1.TaintEffectNoExecute, - }, - }, + }}}}, + }, + fun: func(data args) error { + for i := range data.cluster.Spec.Taints { + if data.cluster.Spec.Taints[i].Effect == corev1.TaintEffectNoExecute && data.cluster.Spec.Taints[i].TimeAdded == nil { + return fmt.Errorf("failed to mutate cluster, taints TimeAdded should not be nil") + } + } + return nil + }, + }, + { + name: "test mutate cluster Zone", + args: args{ + cluster: &clusterapis.Cluster{ + Spec: clusterapis.ClusterSpec{ + Zone: "zone1", }, }, }, + fun: func(data args) error { + if data.cluster.Spec.Zone != "" && len(data.cluster.Spec.Zones) == 0 { + return fmt.Errorf("failed to mutate cluster, zones should not be nil") + } + return nil + }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { MutateCluster(tt.args.cluster) - for i := range tt.args.cluster.Spec.Taints { - if tt.args.cluster.Spec.Taints[i].Effect == corev1.TaintEffectNoExecute && tt.args.cluster.Spec.Taints[i].TimeAdded == nil { - t.Error("failed to mutate cluster, taints TimeAdded should not be nil") - } + if err := tt.fun(tt.args); err != nil { + t.Error(err) } }) } diff --git a/cluster/types.go b/cluster/types.go index b48788e..99ee72a 100644 --- a/cluster/types.go +++ b/cluster/types.go @@ -117,9 +117,19 @@ type ClusterSpec struct { Region string // Zone represents the zone of the member cluster locate in. + // Deprecated: This filed was never been used by Karmada, and it will not be + // removed from v1alpha1 for backward compatibility, use Zones instead. // +optional Zone string + // Zones represents the failure zones(also called availability zones) of the + // member cluster. The zones are presented as a slice to support the case + // that cluster runs across multiple failure zones. + // Refer https://kubernetes.io/docs/setup/best-practices/multiple-zones/ for + // more details about running Kubernetes in multiple zones. + // +optional + Zones []string `json:"zones,omitempty"` + // Taints attached to the member cluster. // Taints on the cluster have the "effect" on // any resource that does not tolerate the Taint. diff --git a/cluster/v1alpha1/types.go b/cluster/v1alpha1/types.go index 9d7ab04..d8bd4e9 100644 --- a/cluster/v1alpha1/types.go +++ b/cluster/v1alpha1/types.go @@ -129,9 +129,19 @@ type ClusterSpec struct { Region string `json:"region,omitempty"` // Zone represents the zone of the member cluster locate in. + // Deprecated: This filed was never been used by Karmada, and it will not be + // removed from v1alpha1 for backward compatibility, use Zones instead. // +optional Zone string `json:"zone,omitempty"` + // Zones represents the failure zones(also called availability zones) of the + // member cluster. The zones are presented as a slice to support the case + // that cluster runs across multiple failure zones. + // Refer https://kubernetes.io/docs/setup/best-practices/multiple-zones/ for + // more details about running Kubernetes in multiple zones. + // +optional + Zones []string `json:"zones,omitempty"` + // Taints attached to the member cluster. // Taints on the cluster have the "effect" on // any resource that does not tolerate the Taint. diff --git a/cluster/v1alpha1/zz_generated.conversion.go b/cluster/v1alpha1/zz_generated.conversion.go index ffcf48e..56e3751 100644 --- a/cluster/v1alpha1/zz_generated.conversion.go +++ b/cluster/v1alpha1/zz_generated.conversion.go @@ -331,6 +331,7 @@ func autoConvert_v1alpha1_ClusterSpec_To_cluster_ClusterSpec(in *ClusterSpec, ou out.Provider = in.Provider out.Region = in.Region out.Zone = in.Zone + out.Zones = *(*[]string)(unsafe.Pointer(&in.Zones)) out.Taints = *(*[]v1.Taint)(unsafe.Pointer(&in.Taints)) out.ResourceModels = *(*[]cluster.ResourceModel)(unsafe.Pointer(&in.ResourceModels)) return nil @@ -353,6 +354,7 @@ func autoConvert_cluster_ClusterSpec_To_v1alpha1_ClusterSpec(in *cluster.Cluster out.Provider = in.Provider out.Region = in.Region out.Zone = in.Zone + out.Zones = *(*[]string)(unsafe.Pointer(&in.Zones)) out.Taints = *(*[]v1.Taint)(unsafe.Pointer(&in.Taints)) out.ResourceModels = *(*[]ResourceModel)(unsafe.Pointer(&in.ResourceModels)) return nil diff --git a/cluster/v1alpha1/zz_generated.deepcopy.go b/cluster/v1alpha1/zz_generated.deepcopy.go index 05c2cc0..82ad95a 100644 --- a/cluster/v1alpha1/zz_generated.deepcopy.go +++ b/cluster/v1alpha1/zz_generated.deepcopy.go @@ -170,6 +170,11 @@ func (in *ClusterSpec) DeepCopyInto(out *ClusterSpec) { (*out)[key] = val } } + if in.Zones != nil { + in, out := &in.Zones, &out.Zones + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.Taints != nil { in, out := &in.Taints, &out.Taints *out = make([]v1.Taint, len(*in)) diff --git a/cluster/zz_generated.deepcopy.go b/cluster/zz_generated.deepcopy.go index fab6e65..e2a990e 100644 --- a/cluster/zz_generated.deepcopy.go +++ b/cluster/zz_generated.deepcopy.go @@ -170,6 +170,11 @@ func (in *ClusterSpec) DeepCopyInto(out *ClusterSpec) { (*out)[key] = val } } + if in.Zones != nil { + in, out := &in.Zones, &out.Zones + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.Taints != nil { in, out := &in.Taints, &out.Taints *out = make([]v1.Taint, len(*in)) diff --git a/config/v1alpha1/interpretercontext_types.go b/config/v1alpha1/interpretercontext_types.go index 452d461..55038ee 100644 --- a/config/v1alpha1/interpretercontext_types.go +++ b/config/v1alpha1/interpretercontext_types.go @@ -153,6 +153,13 @@ type DependentObjectReference struct { Namespace string `json:"namespace,omitempty"` // Name represents the name of the referent. - // +required - Name string `json:"name"` + // Name and LabelSelector cannot be empty at the same time. + // +optional + Name string `json:"name,omitempty"` + + // LabelSelector represents a label query over a set of resources. + // If name is not empty, labelSelector will be ignored. + // Name and LabelSelector cannot be empty at the same time. + // +optional + LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"` } diff --git a/config/v1alpha1/zz_generated.deepcopy.go b/config/v1alpha1/zz_generated.deepcopy.go index b3acfac..6ecb0c6 100644 --- a/config/v1alpha1/zz_generated.deepcopy.go +++ b/config/v1alpha1/zz_generated.deepcopy.go @@ -7,6 +7,7 @@ package v1alpha1 import ( v1alpha2 "github.com/karmada-io/api/work/v1alpha2" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -96,6 +97,11 @@ func (in *DependencyInterpretation) DeepCopy() *DependencyInterpretation { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DependentObjectReference) DeepCopyInto(out *DependentObjectReference) { *out = *in + if in.LabelSelector != nil { + in, out := &in.LabelSelector, &out.LabelSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } return } @@ -368,7 +374,9 @@ func (in *ResourceInterpreterResponse) DeepCopyInto(out *ResourceInterpreterResp if in.Dependencies != nil { in, out := &in.Dependencies, &out.Dependencies *out = make([]DependentObjectReference, len(*in)) - copy(*out, *in) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } if in.RawStatus != nil { in, out := &in.RawStatus, &out.RawStatus diff --git a/go.mod b/go.mod index 5bfc297..7cac4fa 100644 --- a/go.mod +++ b/go.mod @@ -3,26 +3,26 @@ module github.com/karmada-io/api go 1.19 require ( - k8s.io/api v0.26.1 - k8s.io/apiextensions-apiserver v0.26.1 - k8s.io/apimachinery v0.26.1 - k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 - sigs.k8s.io/controller-runtime v0.14.2 + k8s.io/api v0.27.3 + k8s.io/apiextensions-apiserver v0.27.3 + k8s.io/apimachinery v0.27.3 + k8s.io/utils v0.0.0-20230209194617-a36077c30491 + sigs.k8s.io/controller-runtime v0.15.0 ) require ( - github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/logr v1.2.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 // indirect - golang.org/x/text v0.5.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/text v0.9.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/klog/v2 v2.80.1 // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + k8s.io/klog/v2 v2.90.1 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 072d715..9d4bda3 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= @@ -14,19 +14,20 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -38,8 +39,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 h1:Frnccbp+ok2GkUS2tC84yAq/U9Vg+0sIO7aRL3T4Xnc= -golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -48,8 +49,8 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -59,27 +60,27 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -k8s.io/api v0.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ= -k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg= -k8s.io/apiextensions-apiserver v0.26.1 h1:cB8h1SRk6e/+i3NOrQgSFij1B2S0Y0wDoNl66bn8RMI= -k8s.io/apiextensions-apiserver v0.26.1/go.mod h1:AptjOSXDGuE0JICx/Em15PaoO7buLwTs0dGleIHixSM= -k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ= -k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= -k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= -k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= -k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.14.2 h1:P6IwDhbsRWsBClt/8/h8Zy36bCuGuW5Op7MHpFrN/60= -sigs.k8s.io/controller-runtime v0.14.2/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +k8s.io/api v0.27.3 h1:yR6oQXXnUEBWEWcvPWS0jQL575KoAboQPfJAuKNrw5Y= +k8s.io/api v0.27.3/go.mod h1:C4BNvZnQOF7JA/0Xed2S+aUyJSfTGkGFxLXz9MnpIpg= +k8s.io/apiextensions-apiserver v0.27.3 h1:xAwC1iYabi+TDfpRhxh4Eapl14Hs2OftM2DN5MpgKX4= +k8s.io/apiextensions-apiserver v0.27.3/go.mod h1:BH3wJ5NsB9XE1w+R6SSVpKmYNyIiyIz9xAmBl8Mb+84= +k8s.io/apimachinery v0.27.3 h1:Ubye8oBufD04l9QnNtW05idcOe9Z3GQN8+7PqmuVcUM= +k8s.io/apimachinery v0.27.3/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= +k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= +k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= +k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-runtime v0.15.0 h1:ML+5Adt3qZnMSYxZ7gAverBLNPSMQEibtzAgp0UPojU= +sigs.k8s.io/controller-runtime v0.15.0/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= diff --git a/networking/v1alpha1/service_types.go b/networking/v1alpha1/service_types.go new file mode 100644 index 0000000..08f1744 --- /dev/null +++ b/networking/v1alpha1/service_types.go @@ -0,0 +1,108 @@ +package v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + // ResourceKindMultiClusterService is kind name of MultiClusterService. + ResourceKindMultiClusterService = "MultiClusterService" + // ResourceSingularMultiClusterService is singular name of MultiClusterService. + ResourceSingularMultiClusterService = "multiclusterservice" + // ResourcePluralMultiClusterService is plural name of MultiClusterService. + ResourcePluralMultiClusterService = "multiclusterservices" + // ResourceNamespaceScopedMultiClusterService indicates if MultiClusterService is NamespaceScoped. + ResourceNamespaceScopedMultiClusterService = true +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:subresource:status +// +kubebuilder:resource:shortName=mcs,categories={karmada-io} + +// MultiClusterService is a named abstraction of multi-cluster software service. +// The name field of MultiClusterService is the same as that of Service name. +// Services with the same name in different clusters are regarded as the same +// service and are associated with the same MultiClusterService. +// MultiClusterService can control the exposure of services to outside multiple +// clusters, and also enable service discovery between clusters. +type MultiClusterService struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec is the desired state of the MultiClusterService. + Spec MultiClusterServiceSpec `json:"spec"` + + // Status is the current state of the MultiClusterService. + // +optional + Status corev1.ServiceStatus `json:"status,omitempty"` +} + +// MultiClusterServiceSpec is the desired state of the MultiClusterService. +type MultiClusterServiceSpec struct { + // Types specifies how to expose the service referencing by this + // MultiClusterService. + // +required + Types []ExposureType `json:"types"` + + // Ports is the list of ports that are exposed by this MultiClusterService. + // No specified port will be filtered out during the service + // exposure and discovery process. + // All ports in the referencing service will be exposed by default. + // +optional + Ports []ExposurePort `json:"ports,omitempty"` + + // Range specifies the ranges where the referencing service should + // be exposed. + // Only valid and optional in case of Types contains CrossCluster. + // If not set and Types contains CrossCluster, all clusters will + // be selected, that means the referencing service will be exposed + // across all registered clusters. + // +optional + Range ExposureRange `json:"range,omitempty"` +} + +// ExposureType describes how to expose the service. +type ExposureType string + +const ( + // ExposureTypeCrossCluster means a service will be accessible across clusters. + ExposureTypeCrossCluster ExposureType = "CrossCluster" + + // ExposureTypeLoadBalancer means a service will be exposed via an external + // load balancer. + ExposureTypeLoadBalancer ExposureType = "LoadBalancer" +) + +// ExposurePort describes which port will be exposed. +type ExposurePort struct { + // Name is the name of the port that needs to be exposed within the service. + // The port name must be the same as that defined in the service. + // +optional + Name string `json:"name,omitempty"` + + // Port specifies the exposed service port. + // +required + Port int32 `json:"port"` +} + +// ExposureRange describes a list of clusters where the service is exposed. +// Now supports selecting cluster by name, leave the room for extend more methods +// such as using label selector. +type ExposureRange struct { + // ClusterNames is the list of clusters to be selected. + // +optional + ClusterNames []string `json:"clusterNames,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// MultiClusterServiceList is a collection of MultiClusterService. +type MultiClusterServiceList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + // Items is the list of MultiClusterService. + Items []MultiClusterService `json:"items"` +} diff --git a/networking/v1alpha1/zz_generated.deepcopy.go b/networking/v1alpha1/zz_generated.deepcopy.go index 351f6be..944d18e 100644 --- a/networking/v1alpha1/zz_generated.deepcopy.go +++ b/networking/v1alpha1/zz_generated.deepcopy.go @@ -9,6 +9,43 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExposurePort) DeepCopyInto(out *ExposurePort) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExposurePort. +func (in *ExposurePort) DeepCopy() *ExposurePort { + if in == nil { + return nil + } + out := new(ExposurePort) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExposureRange) DeepCopyInto(out *ExposureRange) { + *out = *in + if in.ClusterNames != nil { + in, out := &in.ClusterNames, &out.ClusterNames + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExposureRange. +func (in *ExposureRange) DeepCopy() *ExposureRange { + if in == nil { + return nil + } + out := new(ExposureRange) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MultiClusterIngress) DeepCopyInto(out *MultiClusterIngress) { *out = *in @@ -69,3 +106,91 @@ func (in *MultiClusterIngressList) DeepCopyObject() runtime.Object { } return nil } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MultiClusterService) DeepCopyInto(out *MultiClusterService) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MultiClusterService. +func (in *MultiClusterService) DeepCopy() *MultiClusterService { + if in == nil { + return nil + } + out := new(MultiClusterService) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MultiClusterService) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MultiClusterServiceList) DeepCopyInto(out *MultiClusterServiceList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]MultiClusterService, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MultiClusterServiceList. +func (in *MultiClusterServiceList) DeepCopy() *MultiClusterServiceList { + if in == nil { + return nil + } + out := new(MultiClusterServiceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MultiClusterServiceList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MultiClusterServiceSpec) DeepCopyInto(out *MultiClusterServiceSpec) { + *out = *in + if in.Types != nil { + in, out := &in.Types, &out.Types + *out = make([]ExposureType, len(*in)) + copy(*out, *in) + } + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]ExposurePort, len(*in)) + copy(*out, *in) + } + in.Range.DeepCopyInto(&out.Range) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MultiClusterServiceSpec. +func (in *MultiClusterServiceSpec) DeepCopy() *MultiClusterServiceSpec { + if in == nil { + return nil + } + out := new(MultiClusterServiceSpec) + in.DeepCopyInto(out) + return out +} diff --git a/networking/v1alpha1/zz_generated.register.go b/networking/v1alpha1/zz_generated.register.go index b97ecc9..577164d 100644 --- a/networking/v1alpha1/zz_generated.register.go +++ b/networking/v1alpha1/zz_generated.register.go @@ -44,6 +44,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &MultiClusterIngress{}, &MultiClusterIngressList{}, + &MultiClusterService{}, + &MultiClusterServiceList{}, ) // AddToGroupVersion allows the serialization of client types like ListOptions. v1.AddToGroupVersion(scheme, SchemeGroupVersion) diff --git a/operator/v1alpha1/defaults.go b/operator/v1alpha1/defaults.go index 6b04e14..3a8092b 100644 --- a/operator/v1alpha1/defaults.go +++ b/operator/v1alpha1/defaults.go @@ -19,6 +19,7 @@ var ( karmadaSchedulerImageRepository = fmt.Sprintf("%s/%s", constants.KarmadaDefaultRepository, constants.KarmadaScheduler) karmadaWebhookImageRepository = fmt.Sprintf("%s/%s", constants.KarmadaDefaultRepository, constants.KarmadaWebhook) karmadaDeschedulerImageRepository = fmt.Sprintf("%s/%s", constants.KarmadaDefaultRepository, constants.KarmadaDescheduler) + KarmadaMetricsAdapterImageRepository = fmt.Sprintf("%s/%s", constants.KarmadaDefaultRepository, constants.KarmadaMetricsAdapter) ) func addDefaultingFuncs(scheme *runtime.Scheme) error { @@ -55,6 +56,7 @@ func setDefaultsKarmadaComponents(obj *Karmada) { setDefaultsKarmadaControllerManager(obj.Spec.Components) setDefaultsKarmadaScheduler(obj.Spec.Components) setDefaultsKarmadaWebhook(obj.Spec.Components) + setDefaultsKarmadaMetricsAdapter(obj.Spec.Components) // set addon defaults setDefaultsKarmadaDescheduler(obj.Spec.Components) @@ -227,3 +229,21 @@ func setDefaultsKarmadaDescheduler(obj *KarmadaComponents) { descheduler.Replicas = pointer.Int32(1) } } + +func setDefaultsKarmadaMetricsAdapter(obj *KarmadaComponents) { + if obj.KarmadaMetricsAdapter == nil { + obj.KarmadaMetricsAdapter = &KarmadaMetricsAdapter{} + } + + metricsAdapter := obj.KarmadaMetricsAdapter + if len(metricsAdapter.Image.ImageRepository) == 0 { + metricsAdapter.Image.ImageRepository = KarmadaMetricsAdapterImageRepository + } + if len(metricsAdapter.Image.ImageTag) == 0 { + metricsAdapter.Image.ImageTag = constants.KarmadaDefaultVersion + } + + if metricsAdapter.Replicas == nil { + metricsAdapter.Replicas = pointer.Int32(2) + } +} diff --git a/operator/v1alpha1/helper.go b/operator/v1alpha1/helper.go index e50a838..20ac670 100644 --- a/operator/v1alpha1/helper.go +++ b/operator/v1alpha1/helper.go @@ -2,9 +2,48 @@ package v1alpha1 import ( "fmt" + + apimeta "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // Name returns the image name. func (image *Image) Name() string { return fmt.Sprintf("%s:%s", image.ImageRepository, image.ImageTag) } + +func KarmadaInProgressing(karmada *Karmada, conditionType ConditionType, message string) { + karmada.Status.Conditions = []metav1.Condition{} + newCondition := metav1.Condition{ + Type: string(conditionType), + Status: metav1.ConditionFalse, + Reason: "Progressing", + Message: message, + } + + apimeta.SetStatusCondition(&karmada.Status.Conditions, newCondition) +} + +func KarmadaCompleted(karmada *Karmada, conditionType ConditionType, message string) { + karmada.Status.Conditions = []metav1.Condition{} + newCondition := metav1.Condition{ + Type: string(conditionType), + Status: metav1.ConditionTrue, + Reason: "Completed", + Message: message, + } + + apimeta.SetStatusCondition(&karmada.Status.Conditions, newCondition) +} + +func KarmadaFailed(karmada *Karmada, conditionType ConditionType, message string) { + karmada.Status.Conditions = []metav1.Condition{} + newCondition := metav1.Condition{ + Type: string(conditionType), + Status: metav1.ConditionFalse, + Reason: "Failed", + Message: message, + } + + apimeta.SetStatusCondition(&karmada.Status.Conditions, newCondition) +} diff --git a/operator/v1alpha1/type.go b/operator/v1alpha1/type.go index a2cb314..bd9bf87 100644 --- a/operator/v1alpha1/type.go +++ b/operator/v1alpha1/type.go @@ -25,7 +25,7 @@ import ( // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:subresource:status // +kubebuilder:path=karmadas,scope=Namespaced,categories={karmada-io} -// +kubebuilder:printcolumn:JSONPath=`.status.controlPlaneReady`,name="Status",type=string +// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Ready")].status`,name="Ready",type=string // +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name="Age",type=date // Karmada enables declarative installation of karmada. @@ -66,7 +66,7 @@ type KarmadaSpec struct { // FeatureGates enabled by the user. // - Failover: https://karmada.io/docs/userguide/failover/#failover - // - GragscefulEviction: https://karmada.io/docs/userguide/failover/#graceful-eviction-feature + // - GracefulEviction: https://karmada.io/docs/userguide/failover/#graceful-eviction-feature // - PropagateDeps: https://karmada.io/docs/userguide/scheduling/propagate-dependencies // - CustomizedClusterResourceModeling: https://karmada.io/docs/userguide/scheduling/cluster-resources#start-to-use-cluster-resource-models // More info: https://github.com/karmada-io/karmada/blob/master/pkg/features/features.go @@ -112,17 +112,21 @@ type KarmadaComponents struct { // +optional KarmadaScheduler *KarmadaScheduler `json:"karmadaScheduler,omitempty"` - // KarmadaWebhook holds settings to karmada-webook component of the karmada. + // KarmadaWebhook holds settings to karmada-webhook component of the karmada. // +optional KarmadaWebhook *KarmadaWebhook `json:"karmadaWebhook,omitempty"` // KarmadaDescheduler holds settings to karmada-descheduler component of the karmada. // +optional - KarmadaDescheduler *KarmadaDescheduler `json:"KarmadaDescheduler,omitempty"` + KarmadaDescheduler *KarmadaDescheduler `json:"karmadaDescheduler,omitempty"` // KarmadaSearch holds settings to karmada search component of the karmada. // +optional KarmadaSearch *KarmadaSearch `json:"karmadaSearch,omitempty"` + + // KarmadaMetricsAdapter holds settings to karmada metrics adapter component of the karmada. + // +optional + KarmadaMetricsAdapter *KarmadaMetricsAdapter `json:"karmadaMetricsAdapter,omitempty"` } // Networking contains elements describing cluster's networking configuration @@ -151,7 +155,7 @@ type LocalEtcd struct { CommonSettings `json:",inline"` // VolumeData describes the settings of etcd data store. - // We will support 3 modes: emtydir, hostPath, PVC. default by hostPath. + // We will support 3 modes: emptyDir, hostPath, PVC. default by hostPath. // +optional VolumeData *VolumeData `json:"volumeData,omitempty"` @@ -191,7 +195,7 @@ type VolumeData struct { } // ExternalEtcd describes an external etcd cluster. -// operator has no knowledge of where certificate files live and they must be supplied. +// operator has no knowledge of where certificate files live, and they must be supplied. type ExternalEtcd struct { // Endpoints of etcd members. Required for ExternalEtcd. Endpoints []string `json:"endpoints"` @@ -210,7 +214,7 @@ type ExternalEtcd struct { } // KarmadaAPIServer holds settings to kube-apiserver component of the kubernetes. -// Karmada uses it as it's own apiserver in order to provide Kubernetes-native APIs. +// Karmada uses it as its own apiserver in order to provide Kubernetes-native APIs. type KarmadaAPIServer struct { // CommonSettings holds common settings to kubernetes api server. CommonSettings `json:",inline"` @@ -220,7 +224,7 @@ type KarmadaAPIServer struct { ServiceSubnet *string `json:"serviceSubnet,omitempty"` // ServiceType represents the service type of karmada apiserver. - // it is Nodeport by default. + // it is NodePort by default. // +optional ServiceType corev1.ServiceType `json:"serviceType,omitempty"` @@ -231,7 +235,7 @@ type KarmadaAPIServer struct { // Note: This is a temporary solution to allow for the configuration of the // kube-apiserver component. In the future, we will provide a more structured way // to configure the component. Once that is done, this field will be discouraged to be used. - // Incorrect settings on this feild maybe lead to the corresponding component in an unhealthy + // Incorrect settings on this field maybe lead to the corresponding component in an unhealthy // state. Before you do it, please confirm that you understand the risks of this configuration. // // For supported flags, please see @@ -262,11 +266,11 @@ type KarmadaAggregatedAPIServer struct { // Note: This is a temporary solution to allow for the configuration of the // karmada-aggregated-apiserver component. In the future, we will provide a more structured way // to configure the component. Once that is done, this field will be discouraged to be used. - // Incorrect settings on this feild maybe lead to the corresponding component in an unhealthy + // Incorrect settings on this field maybe lead to the corresponding component in an unhealthy // state. Before you do it, please confirm that you understand the risks of this configuration. // // For supported flags, please see - // https://github.com/karmada-io/karmada/blob/master/cmd/aggregated-apiserver/app/options/options.go + // https://karmada.io/docs/reference/components/karmada-aggregated-apiserver // for details. // +optional ExtraArgs map[string]string `json:"extraArgs,omitempty"` @@ -321,7 +325,7 @@ type KubeControllerManager struct { // https://karmada.io/docs/administrator/configuration/configure-controllers#kubernetes-controllers // // Others are disabled by default. If you want to enable or disable other controllers, you - // have to explicitly specify all the controllers that kube-controller-manager shoud enable + // have to explicitly specify all the controllers that kube-controller-manager should enable // at startup phase. // +optional Controllers []string `json:"controllers,omitempty"` @@ -333,7 +337,7 @@ type KubeControllerManager struct { // Note: This is a temporary solution to allow for the configuration of the // kube-controller-manager component. In the future, we will provide a more structured way // to configure the component. Once that is done, this field will be discouraged to be used. - // Incorrect settings on this feild maybe lead to the corresponding component in an unhealthy + // Incorrect settings on this field maybe lead to the corresponding component in an unhealthy // state. Before you do it, please confirm that you understand the risks of this configuration. // // For supported flags, please see @@ -375,11 +379,11 @@ type KarmadaControllerManager struct { // Note: This is a temporary solution to allow for the configuration of the // karmada-controller-manager component. In the future, we will provide a more structured way // to configure the component. Once that is done, this field will be discouraged to be used. - // Incorrect settings on this feild maybe lead to the corresponding component in an unhealthy + // Incorrect settings on this field maybe lead to the corresponding component in an unhealthy // state. Before you do it, please confirm that you understand the risks of this configuration. // // For supported flags, please see - // https://github.com/karmada-io/karmada/blob/master/cmd/controller-manager/app/options/options.go + // https://karmada.io/docs/reference/components/karmada-controller-manager // for details. // +optional ExtraArgs map[string]string `json:"extraArgs,omitempty"` @@ -406,11 +410,11 @@ type KarmadaScheduler struct { // Note: This is a temporary solution to allow for the configuration of the karmada-scheduler // component. In the future, we will provide a more structured way to configure the component. // Once that is done, this field will be discouraged to be used. - // Incorrect settings on this feild maybe lead to the corresponding component in an unhealthy + // Incorrect settings on this field maybe lead to the corresponding component in an unhealthy // state. Before you do it, please confirm that you understand the risks of this configuration. // // For supported flags, please see - // https://github.com/karmada-io/karmada/blob/master/cmd/scheduler/app/options/options.go + // https://karmada.io/docs/reference/components/karmada-scheduler // for details. // +optional ExtraArgs map[string]string `json:"extraArgs,omitempty"` @@ -434,11 +438,11 @@ type KarmadaDescheduler struct { // Note: This is a temporary solution to allow for the configuration of the karmada-descheduler // component. In the future, we will provide a more structured way to configure the component. // Once that is done, this field will be discouraged to be used. - // Incorrect settings on this feild maybe lead to the corresponding component in an unhealthy + // Incorrect settings on this field maybe lead to the corresponding component in an unhealthy // state. Before you do it, please confirm that you understand the risks of this configuration. // // For supported flags, please see - // https://github.com/karmada-io/karmada/blob/master/cmd/descheduler/app/options/options.go + // https://karmada.io/docs/reference/components/karmada-descheduler // for details. // +optional ExtraArgs map[string]string `json:"extraArgs,omitempty"` @@ -449,18 +453,40 @@ type KarmadaSearch struct { // CommonSettings holds common settings to karmada search. CommonSettings `json:",inline"` - // ExtraArgs is an extra set of flags to pass to the karmada-descheduler component or override. + // ExtraArgs is an extra set of flags to pass to the karmada-search component or override. // A key in this map is the flag name as it appears on the command line except without // leading dash(es). // - // Note: This is a temporary solution to allow for the configuration of the karmada-descheduler + // Note: This is a temporary solution to allow for the configuration of the karmada-search // component. In the future, we will provide a more structured way to configure the component. // Once that is done, this field will be discouraged to be used. - // Incorrect settings on this feild maybe lead to the corresponding component in an unhealthy + // Incorrect settings on this field maybe lead to the corresponding component in an unhealthy // state. Before you do it, please confirm that you understand the risks of this configuration. // // For supported flags, please see - // https://github.com/karmada-io/karmada/blob/master/cmd/descheduler/app/options/options.go + // https://karmada.io/docs/reference/components/karmada-search + // for details. + // +optional + ExtraArgs map[string]string `json:"extraArgs,omitempty"` +} + +// KarmadaMetricsAdapter holds settings to karmada-metrics-adapter component of the karmada. +type KarmadaMetricsAdapter struct { + // CommonSettings holds common settings to karmada metrics adapter. + CommonSettings `json:",inline"` + + // ExtraArgs is an extra set of flags to pass to the karmada-metrics-adapter component or override. + // A key in this map is the flag name as it appears on the command line except without + // leading dash(es). + // + // Note: This is a temporary solution to allow for the configuration of the karmada-metrics-adapter + // component. In the future, we will provide a more structured way to configure the component. + // Once that is done, this field will be discouraged to be used. + // Incorrect settings on this field maybe lead to the corresponding component in an unhealthy + // state. Before you do it, please confirm that you understand the risks of this configuration. + // + // For supported flags, please see + // https://karmada.io/docs/reference/components/karmada-metrics-adapter // for details. // +optional ExtraArgs map[string]string `json:"extraArgs,omitempty"` @@ -478,11 +504,11 @@ type KarmadaWebhook struct { // Note: This is a temporary solution to allow for the configuration of the // karmada-webhook component. In the future, we will provide a more structured way // to configure the component. Once that is done, this field will be discouraged to be used. - // Incorrect settings on this feild maybe lead to the corresponding component in an unhealthy + // Incorrect settings on this field maybe lead to the corresponding component in an unhealthy // state. Before you do it, please confirm that you understand the risks of this configuration. // // For supported flags, please see - // https://github.com/karmada-io/karmada/blob/master/cmd/webhook/app/options/options.go + // https://karmada.io/docs/reference/components/karmada-webhook // for details. // +optional ExtraArgs map[string]string `json:"extraArgs,omitempty"` @@ -553,19 +579,15 @@ type HostCluster struct { Networking *Networking `json:"networking,omitempty"` } -// ConditionType declarative karmada condition type of karmada installtion. +// ConditionType declarative karmada condition type of karmada installation. type ConditionType string const ( - // Unknown represent a condition type the karmada not be reconciled by operator - // or unpredictable condition. - Unknown ConditionType = "Unknown" - - // Ready represent a condition type the all installtion process to karmada have compaleted. + // Ready represent a condition type the all installation process to karmada have completed. Ready ConditionType = "Ready" ) -// KarmadaStatus difine the most recently observed status of the Karmada. +// KarmadaStatus define the most recently observed status of the Karmada. type KarmadaStatus struct { // ObservedGeneration is the last observed generation. // +optional @@ -575,11 +597,11 @@ type KarmadaStatus struct { // +optional SecretRef *LocalSecretReference `json:"secretRef,omitempty"` - // KarmadaVersion represente the karmada version. + // KarmadaVersion represent the karmada version. // +optional KarmadaVersion string `json:"karmadaVersion,omitempty"` - // KubernetesVersion represente the karmada-apiserver version. + // KubernetesVersion represent the karmada-apiserver version. // +optional KubernetesVersion string `json:"kubernetesVersion,omitempty"` diff --git a/operator/v1alpha1/zz_generated.deepcopy.go b/operator/v1alpha1/zz_generated.deepcopy.go index 8f7025e..43922d2 100644 --- a/operator/v1alpha1/zz_generated.deepcopy.go +++ b/operator/v1alpha1/zz_generated.deepcopy.go @@ -321,6 +321,11 @@ func (in *KarmadaComponents) DeepCopyInto(out *KarmadaComponents) { *out = new(KarmadaSearch) (*in).DeepCopyInto(*out) } + if in.KarmadaMetricsAdapter != nil { + in, out := &in.KarmadaMetricsAdapter, &out.KarmadaMetricsAdapter + *out = new(KarmadaMetricsAdapter) + (*in).DeepCopyInto(*out) + } return } @@ -427,6 +432,30 @@ func (in *KarmadaList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KarmadaMetricsAdapter) DeepCopyInto(out *KarmadaMetricsAdapter) { + *out = *in + in.CommonSettings.DeepCopyInto(&out.CommonSettings) + if in.ExtraArgs != nil { + in, out := &in.ExtraArgs, &out.ExtraArgs + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KarmadaMetricsAdapter. +func (in *KarmadaMetricsAdapter) DeepCopy() *KarmadaMetricsAdapter { + if in == nil { + return nil + } + out := new(KarmadaMetricsAdapter) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KarmadaScheduler) DeepCopyInto(out *KarmadaScheduler) { *out = *in diff --git a/policy/v1alpha1/propagation_types.go b/policy/v1alpha1/propagation_types.go index 36de0a6..5976f2e 100644 --- a/policy/v1alpha1/propagation_types.go +++ b/policy/v1alpha1/propagation_types.go @@ -76,6 +76,7 @@ type PropagationSpec struct { // template be processed. // Once a resource template has been claimed by a policy, by default it will // not be preempted by following policies even with a higher priority. + // See Preemption for more details. // // In case of two policies have the same priority, the one with a more precise // matching rules in ResourceSelectors wins: @@ -91,6 +92,14 @@ type PropagationSpec struct { // +kubebuilder:default=0 Priority *int32 `json:"priority,omitempty"` + // Preemption declares the behaviors for preempting. + // Valid options are "Always" and "Never". + // + // +kubebuilder:default="Never" + // +kubebuilder:validation:Enum=Always;Never + // +optional + Preemption PreemptionBehavior `json:"preemption,omitempty"` + // DependentOverrides represents the list of overrides(OverridePolicy) // which must present before the current PropagationPolicy takes effect. // @@ -114,6 +123,19 @@ type PropagationSpec struct { // If this value is nil, failover is disabled. // +optional Failover *FailoverBehavior `json:"failover,omitempty"` + + // ConflictResolution declares how potential conflict should be handled when + // a resource that is being propagated already exists in the target cluster. + // + // It defaults to "Abort" which means stop propagating to avoid unexpected + // overwrites. The "Overwrite" might be useful when migrating legacy cluster + // resources to Karmada, in which case conflict is predictable and can be + // instructed to Karmada take over the resource by overwriting. + // + // +kubebuilder:default="Abort" + // +kubebuilder:validation:Enum=Abort;Overwrite + // +optional + ConflictResolution ConflictResolution `json:"conflictResolution,omitempty"` } // ResourceSelector the resources will be selected. @@ -443,6 +465,43 @@ const ( DynamicWeightByAvailableReplicas DynamicWeightFactor = "AvailableReplicas" ) +// PreemptionBehavior describes whether and how to preempt resources that are +// claimed by lower-priority PropagationPolicy(ClusterPropagationPolicy). +// +enum +type PreemptionBehavior string + +const ( + // PreemptAlways means that preemption is allowed. + // + // If it is applied to a PropagationPolicy, it can preempt any resource as + // per Priority, regardless of whether it has been claimed by a PropagationPolicy + // or a ClusterPropagationPolicy, as long as it can match the rules defined + // in ResourceSelector. In addition, if a resource has already been claimed + // by a ClusterPropagationPolicy, the PropagationPolicy can still preempt it + // without considering Priority. + // + // If it is applied to a ClusterPropagationPolicy, it can only preempt from + // ClusterPropagationPolicy, and from PropagationPolicy is not allowed. + PreemptAlways PreemptionBehavior = "Always" + + // PreemptNever means that a PropagationPolicy(ClusterPropagationPolicy) never + // preempts resources. + PreemptNever PreemptionBehavior = "Never" +) + +// ConflictResolution describes how to resolve the conflict during the process +// of propagation especially the resource already in a member cluster. +type ConflictResolution string + +const ( + // ConflictOverwrite means that resolve the conflict by overwriting the + // resource with the propagating resource template. + ConflictOverwrite ConflictResolution = "Overwrite" + + // ConflictAbort means that do not resolve the conflict and stop propagating. + ConflictAbort ConflictResolution = "Abort" +) + // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // PropagationPolicyList contains a list of PropagationPolicy. diff --git a/policy/v1alpha1/well_known_constants.go b/policy/v1alpha1/well_known_constants.go index fd55be5..144f5d8 100644 --- a/policy/v1alpha1/well_known_constants.go +++ b/policy/v1alpha1/well_known_constants.go @@ -1,6 +1,21 @@ package v1alpha1 const ( + // PropagationPolicyUIDLabel is the uid of PropagationPolicy object. + PropagationPolicyUIDLabel = "propagationpolicy.karmada.io/uid" + + // PropagationPolicyNamespaceAnnotation is added to objects to specify associated PropagationPolicy namespace. + PropagationPolicyNamespaceAnnotation = "propagationpolicy.karmada.io/namespace" + + // PropagationPolicyNameAnnotation is added to objects to specify associated PropagationPolicy name. + PropagationPolicyNameAnnotation = "propagationpolicy.karmada.io/name" + + // ClusterPropagationPolicyUIDLabel is the uid of ClusterPropagationPolicy object. + ClusterPropagationPolicyUIDLabel = "clusterpropagationpolicy.karmada.io/uid" + + // ClusterPropagationPolicyAnnotation is added to objects to specify associated ClusterPropagationPolicy name. + ClusterPropagationPolicyAnnotation = "clusterpropagationpolicy.karmada.io/name" + // PropagationPolicyNamespaceLabel is added to objects to specify associated PropagationPolicy namespace. PropagationPolicyNamespaceLabel = "propagationpolicy.karmada.io/namespace" diff --git a/search/scheme/register.go b/search/scheme/register.go index 86f9d82..55e4810 100644 --- a/search/scheme/register.go +++ b/search/scheme/register.go @@ -29,14 +29,4 @@ func init() { // we need to add the options to empty v1 // TODO fix the server code to avoid this metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) - - // TODO: keep the generic API server from wanting this - unversioned := schema.GroupVersion{Group: "", Version: "v1"} - Scheme.AddUnversionedTypes(unversioned, - &metav1.Status{}, - &metav1.APIVersions{}, - &metav1.APIGroupList{}, - &metav1.APIGroup{}, - &metav1.APIResourceList{}, - ) } diff --git a/work/v1alpha2/binding_types.go b/work/v1alpha2/binding_types.go index 545a999..7265df3 100644 --- a/work/v1alpha2/binding_types.go +++ b/work/v1alpha2/binding_types.go @@ -107,6 +107,19 @@ type ResourceBindingSpec struct { // It inherits directly from the associated PropagationPolicy(or ClusterPropagationPolicy). // +optional Failover *policyv1alpha1.FailoverBehavior `json:"failover,omitempty"` + + // ConflictResolution declares how potential conflict should be handled when + // a resource that is being propagated already exists in the target cluster. + // + // It defaults to "Abort" which means stop propagating to avoid unexpected + // overwrites. The "Overwrite" might be useful when migrating legacy cluster + // resources to Karmada, in which case conflict is predictable and can be + // instructed to Karmada take over the resource by overwriting. + // + // +kubebuilder:default="Abort" + // +kubebuilder:validation:Enum=Abort;Overwrite + // +optional + ConflictResolution policyv1alpha1.ConflictResolution `json:"conflictResolution,omitempty"` } // ObjectReference contains enough information to locate the referenced object inside current cluster. @@ -307,6 +320,24 @@ const ( FullyApplied string = "FullyApplied" ) +// These are reasons for a binding's transition to a Scheduled condition. +const ( + // BindingReasonSuccess reason in Scheduled condition means that binding has been scheduled successfully. + BindingReasonSuccess = "Success" + + // BindingReasonSchedulerError reason in Scheduled condition means that some internal error happens + // during scheduling, for example due to api-server connection error. + BindingReasonSchedulerError = "SchedulerError" + + // BindingReasonNoClusterFit reason in Scheduled condition means that scheduling has finished + // due to no fit cluster. + BindingReasonNoClusterFit = "NoClusterFit" + + // BindingReasonUnschedulable reason in Scheduled condition means that the scheduler can't schedule + // the binding right now, for example due to insufficient resources in the clusters. + BindingReasonUnschedulable = "Unschedulable" +) + // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // ResourceBindingList contains a list of ResourceBinding. diff --git a/work/v1alpha2/well_known_constants.go b/work/v1alpha2/well_known_constants.go index ebe6130..237a1f8 100644 --- a/work/v1alpha2/well_known_constants.go +++ b/work/v1alpha2/well_known_constants.go @@ -1,6 +1,21 @@ package v1alpha2 const ( + // ResourceBindingUIDLabel is the UID of ResourceBinding object. + ResourceBindingUIDLabel = "resourcebinding.karmada.io/uid" + + // ClusterResourceBindingUIDLabel is the uid of ClusterResourceBinding object. + ClusterResourceBindingUIDLabel = "clusterresourcebinding.karmada.io/uid" + + // WorkNamespaceAnnotation is added to objects to specify associated Work's namespace. + WorkNamespaceAnnotation = "work.karmada.io/namespace" + + // WorkNameAnnotation is added to objects to specify associated Work's name. + WorkNameAnnotation = "work.karmada.io/name" + + // WorkUIDLabel is the uid of Work object. + WorkUIDLabel = "work.karmada.io/uid" + // ResourceBindingReferenceKey is the key of ResourceBinding object. // It is usually a unique hash value of ResourceBinding object's namespace and name, intended to be added to the Work object. // It will be used to retrieve all Works objects that derived from a specific ResourceBinding object. @@ -43,11 +58,15 @@ const ( // The valid value is: // - overwrite: always overwrite the resource if already exist. The resource will be overwritten with the // configuration from control plane. + // - abort: do not resolve the conflict and stop propagating to avoid unexpected overwrites (default value) // Note: Propagation of the resource template without this annotation will fail in case of already exists. ResourceConflictResolutionAnnotation = "work.karmada.io/conflict-resolution" - // ResourceConflictResolutionOverwrite is the value of ResourceConflictResolutionAnnotation, indicating the overwrite strategy. + // ResourceConflictResolutionOverwrite is a value of ResourceConflictResolutionAnnotation, indicating the overwrite strategy. ResourceConflictResolutionOverwrite = "overwrite" + + // ResourceConflictResolutionAbort is a value of ResourceConflictResolutionAnnotation, indicating stop propagating. + ResourceConflictResolutionAbort = "abort" ) // Define annotations that are added to the resource template.