From b43c4c417b3a8f368edfbcf2b5cee75485fce3ab Mon Sep 17 00:00:00 2001 From: Britania Rodriguez Reyes Date: Thu, 13 Nov 2025 12:20:43 -0800 Subject: [PATCH] fix scheduler for future implementation Signed-off-by: Britania Rodriguez Reyes --- .../plugins/clusteraffinity/filtering.go | 4 +- .../plugins/clusteraffinity/plugin.go | 4 +- .../plugins/clusteraffinity/types.go | 13 +- .../plugins/clusteraffinity/types_test.go | 336 ++++++++++-------- pkg/scheduler/profile/profile.go | 11 + 5 files changed, 214 insertions(+), 154 deletions(-) diff --git a/pkg/scheduler/framework/plugins/clusteraffinity/filtering.go b/pkg/scheduler/framework/plugins/clusteraffinity/filtering.go index 992f1eefa..d25f198e4 100644 --- a/pkg/scheduler/framework/plugins/clusteraffinity/filtering.go +++ b/pkg/scheduler/framework/plugins/clusteraffinity/filtering.go @@ -59,7 +59,9 @@ func (p *Plugin) Filter( for idx := range ps.GetPolicySnapshotSpec().Policy.Affinity.ClusterAffinity.RequiredDuringSchedulingIgnoredDuringExecution.ClusterSelectorTerms { t := &ps.GetPolicySnapshotSpec().Policy.Affinity.ClusterAffinity.RequiredDuringSchedulingIgnoredDuringExecution.ClusterSelectorTerms[idx] - r := clusterRequirement(*t) + r := clusterRequirement{ + ClusterSelectorTerm: *t, + } isMatched, err := r.Matches(cluster) if err != nil { // An error has occurred when matching the cluster against a required affinity term. diff --git a/pkg/scheduler/framework/plugins/clusteraffinity/plugin.go b/pkg/scheduler/framework/plugins/clusteraffinity/plugin.go index 75cb43a80..b0ede644b 100644 --- a/pkg/scheduler/framework/plugins/clusteraffinity/plugin.go +++ b/pkg/scheduler/framework/plugins/clusteraffinity/plugin.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package clusteraffinity features a scheduler plugin that enforces cluster affinity (if any) defined on a CRP. +// Package clusteraffinity features a scheduler plugin that enforces cluster affinity (if any) defined on a RP/CRP. package clusteraffinity import ( @@ -24,7 +24,7 @@ import ( "github.com/kubefleet-dev/kubefleet/pkg/scheduler/framework" ) -// Plugin is the scheduler plugin that enforces the cluster affinity (if any) defined on a CRP. +// Plugin is the scheduler plugin that enforces the cluster affinity (if any) defined on a R/CRP. type Plugin struct { // The name of the plugin. name string diff --git a/pkg/scheduler/framework/plugins/clusteraffinity/types.go b/pkg/scheduler/framework/plugins/clusteraffinity/types.go index daa6a1b0e..6435c1a72 100644 --- a/pkg/scheduler/framework/plugins/clusteraffinity/types.go +++ b/pkg/scheduler/framework/plugins/clusteraffinity/types.go @@ -33,7 +33,10 @@ import ( // clusterRequirement is a type alias for ClusterSelectorTerm in the API, which allows // easy method extension. -type clusterRequirement placementv1beta1.ClusterSelectorTerm +type clusterRequirement struct { + // Embed the original ClusterSelectorTerm. + ClusterSelectorTerm placementv1beta1.ClusterSelectorTerm +} // retrieveResourceUsageFrom retrieves a resource property value from a member cluster. // @@ -121,8 +124,8 @@ func retrievePropertyValueFrom(cluster *clusterv1beta1.MemberCluster, name strin // This is an extended method for the ClusterSelectorTerm API. func (c *clusterRequirement) Matches(cluster *clusterv1beta1.MemberCluster) (bool, error) { // Match the cluster against the label selector. - if c.LabelSelector != nil { - ls, err := metav1.LabelSelectorAsSelector(c.LabelSelector) + if c.ClusterSelectorTerm.LabelSelector != nil { + ls, err := metav1.LabelSelectorAsSelector(c.ClusterSelectorTerm.LabelSelector) if err != nil { return false, fmt.Errorf("failed to parse label selector: %w", err) } @@ -134,12 +137,12 @@ func (c *clusterRequirement) Matches(cluster *clusterv1beta1.MemberCluster) (boo } // Match the cluster against the property selector. - if c.PropertySelector == nil || len(c.PropertySelector.MatchExpressions) == 0 { + if c.ClusterSelectorTerm.PropertySelector == nil || len(c.ClusterSelectorTerm.PropertySelector.MatchExpressions) == 0 { // The term does not feature a property selector; no check is needed. return true, nil } - for _, exp := range c.PropertySelector.MatchExpressions { + for _, exp := range c.ClusterSelectorTerm.PropertySelector.MatchExpressions { // Compare the observed value with the expected one using the specified operator. q, err := retrievePropertyValueFrom(cluster, exp.Name) if err != nil { diff --git a/pkg/scheduler/framework/plugins/clusteraffinity/types_test.go b/pkg/scheduler/framework/plugins/clusteraffinity/types_test.go index e2885105f..eccbe7f88 100644 --- a/pkg/scheduler/framework/plugins/clusteraffinity/types_test.go +++ b/pkg/scheduler/framework/plugins/clusteraffinity/types_test.go @@ -269,13 +269,15 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "invalid label selector", clusterRequirement: &clusterRequirement{ - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: regionLabelName, - Operator: metav1.LabelSelectorOperator("invalid"), - Values: []string{ - regionLabelValue1, + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + LabelSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: regionLabelName, + Operator: metav1.LabelSelectorOperator("invalid"), + Values: []string{ + regionLabelValue1, + }, }, }, }, @@ -287,9 +289,11 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "label selector mismatches", clusterRequirement: &clusterRequirement{ - LabelSelector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - envLabelName: envLabelValue2, + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + envLabelName: envLabelValue2, + }, }, }, }, @@ -299,9 +303,11 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "label selector matches, no property selector", clusterRequirement: &clusterRequirement{ - LabelSelector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - envLabelName: envLabelValue1, + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + envLabelName: envLabelValue1, + }, }, }, }, @@ -311,13 +317,15 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "label selector matches, no expressions in the property selector", clusterRequirement: &clusterRequirement{ - LabelSelector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - envLabelName: envLabelValue1, + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + envLabelName: envLabelValue1, + }, + }, + PropertySelector: &placementv1beta1.PropertySelector{ + MatchExpressions: []placementv1beta1.PropertySelectorRequirement{}, }, - }, - PropertySelector: &placementv1beta1.PropertySelector{ - MatchExpressions: []placementv1beta1.PropertySelectorRequirement{}, }, }, cluster: cluster, @@ -326,13 +334,15 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "invalid resource property name", clusterRequirement: &clusterRequirement{ - PropertySelector: &placementv1beta1.PropertySelector{ - MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ - { - Name: "resources.kubernetes-fleet.io/cpu", - Operator: placementv1beta1.PropertySelectorEqualTo, - Values: []string{ - "2", + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + PropertySelector: &placementv1beta1.PropertySelector{ + MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ + { + Name: "resources.kubernetes-fleet.io/cpu", + Operator: placementv1beta1.PropertySelectorEqualTo, + Values: []string{ + "2", + }, }, }, }, @@ -344,13 +354,15 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "property not found", clusterRequirement: &clusterRequirement{ - PropertySelector: &placementv1beta1.PropertySelector{ - MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ - { - Name: nonExistentNonResourcePropertyName, - Operator: placementv1beta1.PropertySelectorEqualTo, - Values: []string{ - "0", + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + PropertySelector: &placementv1beta1.PropertySelector{ + MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ + { + Name: nonExistentNonResourcePropertyName, + Operator: placementv1beta1.PropertySelectorEqualTo, + Values: []string{ + "0", + }, }, }, }, @@ -361,14 +373,16 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "multiple value options", clusterRequirement: &clusterRequirement{ - PropertySelector: &placementv1beta1.PropertySelector{ - MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ - { - Name: propertyprovider.NodeCountProperty, - Operator: placementv1beta1.PropertySelectorEqualTo, - Values: []string{ - "1", - "2", + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + PropertySelector: &placementv1beta1.PropertySelector{ + MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ + { + Name: propertyprovider.NodeCountProperty, + Operator: placementv1beta1.PropertySelectorEqualTo, + Values: []string{ + "1", + "2", + }, }, }, }, @@ -380,13 +394,15 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "invalid property value", clusterRequirement: &clusterRequirement{ - PropertySelector: &placementv1beta1.PropertySelector{ - MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ - { - Name: invalidNonResourcePropertyName, - Operator: placementv1beta1.PropertySelectorEqualTo, - Values: []string{ - "1", + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + PropertySelector: &placementv1beta1.PropertySelector{ + MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ + { + Name: invalidNonResourcePropertyName, + Operator: placementv1beta1.PropertySelectorEqualTo, + Values: []string{ + "1", + }, }, }, }, @@ -398,13 +414,15 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "invalid value option", clusterRequirement: &clusterRequirement{ - PropertySelector: &placementv1beta1.PropertySelector{ - MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ - { - Name: propertyprovider.NodeCountProperty, - Operator: placementv1beta1.PropertySelectorEqualTo, - Values: []string{ - "invalid", + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + PropertySelector: &placementv1beta1.PropertySelector{ + MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ + { + Name: propertyprovider.NodeCountProperty, + Operator: placementv1beta1.PropertySelectorEqualTo, + Values: []string{ + "invalid", + }, }, }, }, @@ -416,13 +434,15 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "invalid operator", clusterRequirement: &clusterRequirement{ - PropertySelector: &placementv1beta1.PropertySelector{ - MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ - { - Name: propertyprovider.NodeCountProperty, - Operator: "invalid", - Values: []string{ - "1", + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + PropertySelector: &placementv1beta1.PropertySelector{ + MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ + { + Name: propertyprovider.NodeCountProperty, + Operator: "invalid", + Values: []string{ + "1", + }, }, }, }, @@ -434,13 +454,15 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "op =, matched", clusterRequirement: &clusterRequirement{ - PropertySelector: &placementv1beta1.PropertySelector{ - MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ - { - Name: propertyprovider.NodeCountProperty, - Operator: placementv1beta1.PropertySelectorEqualTo, - Values: []string{ - "4", + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + PropertySelector: &placementv1beta1.PropertySelector{ + MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ + { + Name: propertyprovider.NodeCountProperty, + Operator: placementv1beta1.PropertySelectorEqualTo, + Values: []string{ + "4", + }, }, }, }, @@ -452,13 +474,15 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "op =, not matched", clusterRequirement: &clusterRequirement{ - PropertySelector: &placementv1beta1.PropertySelector{ - MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ - { - Name: propertyprovider.NodeCountProperty, - Operator: placementv1beta1.PropertySelectorEqualTo, - Values: []string{ - "8", + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + PropertySelector: &placementv1beta1.PropertySelector{ + MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ + { + Name: propertyprovider.NodeCountProperty, + Operator: placementv1beta1.PropertySelectorEqualTo, + Values: []string{ + "8", + }, }, }, }, @@ -469,13 +493,15 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "op !=, matched", clusterRequirement: &clusterRequirement{ - PropertySelector: &placementv1beta1.PropertySelector{ - MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ - { - Name: propertyprovider.TotalCPUCapacityProperty, - Operator: placementv1beta1.PropertySelectorNotEqualTo, - Values: []string{ - "11", + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + PropertySelector: &placementv1beta1.PropertySelector{ + MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ + { + Name: propertyprovider.TotalCPUCapacityProperty, + Operator: placementv1beta1.PropertySelectorNotEqualTo, + Values: []string{ + "11", + }, }, }, }, @@ -487,13 +513,15 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "op !=, not matched", clusterRequirement: &clusterRequirement{ - PropertySelector: &placementv1beta1.PropertySelector{ - MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ - { - Name: propertyprovider.TotalCPUCapacityProperty, - Operator: placementv1beta1.PropertySelectorNotEqualTo, - Values: []string{ - "10", + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + PropertySelector: &placementv1beta1.PropertySelector{ + MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ + { + Name: propertyprovider.TotalCPUCapacityProperty, + Operator: placementv1beta1.PropertySelectorNotEqualTo, + Values: []string{ + "10", + }, }, }, }, @@ -504,13 +532,15 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "op >, matched", clusterRequirement: &clusterRequirement{ - PropertySelector: &placementv1beta1.PropertySelector{ - MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ - { - Name: propertyprovider.AllocatableMemoryCapacityProperty, - Operator: placementv1beta1.PropertySelectorGreaterThan, - Values: []string{ - "30Gi", + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + PropertySelector: &placementv1beta1.PropertySelector{ + MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ + { + Name: propertyprovider.AllocatableMemoryCapacityProperty, + Operator: placementv1beta1.PropertySelectorGreaterThan, + Values: []string{ + "30Gi", + }, }, }, }, @@ -522,13 +552,15 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "op >, not matched", clusterRequirement: &clusterRequirement{ - PropertySelector: &placementv1beta1.PropertySelector{ - MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ - { - Name: propertyprovider.AllocatableMemoryCapacityProperty, - Operator: placementv1beta1.PropertySelectorGreaterThan, - Values: []string{ - "40Gi", + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + PropertySelector: &placementv1beta1.PropertySelector{ + MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ + { + Name: propertyprovider.AllocatableMemoryCapacityProperty, + Operator: placementv1beta1.PropertySelectorGreaterThan, + Values: []string{ + "40Gi", + }, }, }, }, @@ -539,13 +571,15 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "op <, matched", clusterRequirement: &clusterRequirement{ - PropertySelector: &placementv1beta1.PropertySelector{ - MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ - { - Name: propertyprovider.AvailableCPUCapacityProperty, - Operator: placementv1beta1.PropertySelectorLessThan, - Values: []string{ - "4", + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + PropertySelector: &placementv1beta1.PropertySelector{ + MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ + { + Name: propertyprovider.AvailableCPUCapacityProperty, + Operator: placementv1beta1.PropertySelectorLessThan, + Values: []string{ + "4", + }, }, }, }, @@ -557,13 +591,15 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "op <, not matched", clusterRequirement: &clusterRequirement{ - PropertySelector: &placementv1beta1.PropertySelector{ - MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ - { - Name: propertyprovider.AvailableCPUCapacityProperty, - Operator: placementv1beta1.PropertySelectorLessThan, - Values: []string{ - "1", + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + PropertySelector: &placementv1beta1.PropertySelector{ + MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ + { + Name: propertyprovider.AvailableCPUCapacityProperty, + Operator: placementv1beta1.PropertySelectorLessThan, + Values: []string{ + "1", + }, }, }, }, @@ -574,13 +610,15 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "op >=, matched", clusterRequirement: &clusterRequirement{ - PropertySelector: &placementv1beta1.PropertySelector{ - MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ - { - Name: propertyprovider.TotalMemoryCapacityProperty, - Operator: placementv1beta1.PropertySelectorGreaterThanOrEqualTo, - Values: []string{ - "40Gi", + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + PropertySelector: &placementv1beta1.PropertySelector{ + MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ + { + Name: propertyprovider.TotalMemoryCapacityProperty, + Operator: placementv1beta1.PropertySelectorGreaterThanOrEqualTo, + Values: []string{ + "40Gi", + }, }, }, }, @@ -592,13 +630,15 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "op >=, not matched", clusterRequirement: &clusterRequirement{ - PropertySelector: &placementv1beta1.PropertySelector{ - MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ - { - Name: propertyprovider.TotalMemoryCapacityProperty, - Operator: placementv1beta1.PropertySelectorGreaterThanOrEqualTo, - Values: []string{ - "41Gi", + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + PropertySelector: &placementv1beta1.PropertySelector{ + MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ + { + Name: propertyprovider.TotalMemoryCapacityProperty, + Operator: placementv1beta1.PropertySelectorGreaterThanOrEqualTo, + Values: []string{ + "41Gi", + }, }, }, }, @@ -609,13 +649,15 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "op <=, matched", clusterRequirement: &clusterRequirement{ - PropertySelector: &placementv1beta1.PropertySelector{ - MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ - { - Name: propertyprovider.AllocatableCPUCapacityProperty, - Operator: placementv1beta1.PropertySelectorLessThanOrEqualTo, - Values: []string{ - "8", + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + PropertySelector: &placementv1beta1.PropertySelector{ + MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ + { + Name: propertyprovider.AllocatableCPUCapacityProperty, + Operator: placementv1beta1.PropertySelectorLessThanOrEqualTo, + Values: []string{ + "8", + }, }, }, }, @@ -627,13 +669,15 @@ func TestClusterRequirementMatches(t *testing.T) { { name: "op <=, not matched", clusterRequirement: &clusterRequirement{ - PropertySelector: &placementv1beta1.PropertySelector{ - MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ - { - Name: propertyprovider.AllocatableCPUCapacityProperty, - Operator: placementv1beta1.PropertySelectorLessThanOrEqualTo, - Values: []string{ - "7", + ClusterSelectorTerm: placementv1beta1.ClusterSelectorTerm{ + PropertySelector: &placementv1beta1.PropertySelector{ + MatchExpressions: []placementv1beta1.PropertySelectorRequirement{ + { + Name: propertyprovider.AllocatableCPUCapacityProperty, + Operator: placementv1beta1.PropertySelectorLessThanOrEqualTo, + Values: []string{ + "7", + }, }, }, }, diff --git a/pkg/scheduler/profile/profile.go b/pkg/scheduler/profile/profile.go index 53378d79b..c05b89cc8 100644 --- a/pkg/scheduler/profile/profile.go +++ b/pkg/scheduler/profile/profile.go @@ -31,12 +31,23 @@ const ( defaultProfileName = "DefaultProfile" ) +type Options struct { + ClusterAffinityPlugin *clusteraffinity.Plugin +} + // NewDefaultProfile creates a default scheduling profile. func NewDefaultProfile() *framework.Profile { + return NewProfile(Options{}) +} + +func NewProfile(opts Options) *framework.Profile { p := framework.NewProfile(defaultProfileName) // default plugin list clusterAffinityPlugin := clusteraffinity.New() + if opts.ClusterAffinityPlugin != nil { + clusterAffinityPlugin = *opts.ClusterAffinityPlugin + } clusterEligibilityPlugin := clustereligibility.New() samePlacementAffinityPlugin := sameplacementaffinity.New() topologySpreadConstraintsPlugin := topologyspreadconstraints.New()