@@ -2,6 +2,7 @@ package config
22
33import (
44 "fmt"
5+ "maps"
56 "slices"
67
78 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -24,10 +25,13 @@ type SchedulerConfig struct {
2425 Strategy Strategy `json:"strategy"`
2526
2627 // +optional
27- Selectors * SchedulerSelectors `json:"selectors,omitempty"`
28+ Selectors * SchedulerSelectors `json:"selectors,omitempty"`
29+ // Note that CompletedSelectors.Clusters holds the global cluster selector.
30+ // During Complete(), the local selector is merged with the global one (or set to the global one if nil).
31+ // This means that always the local completed selector should be used, unless the task is not tied to a specific ClusterDefinition.
2832 CompletedSelectors CompletedSchedulerSelectors `json:"-"`
2933
30- PurposeMappings map [string ]ClusterDefinition `json:"purposeMappings"`
34+ PurposeMappings map [string ]* ClusterDefinition `json:"purposeMappings"`
3135}
3236
3337type SchedulerScope string
@@ -51,7 +55,9 @@ type ClusterDefinition struct {
5155 // Must be equal to or greater than 0 otherwise, with 0 meaning "unlimited".
5256 TenancyCount int `json:"tenancyCount,omitempty"`
5357
54- Template ClusterTemplate `json:"template"`
58+ Template ClusterTemplate `json:"template"`
59+ Selector * metav1.LabelSelector `json:"selector,omitempty"`
60+ CompletedSelector labels.Selector `json:"-"`
5561}
5662
5763type ClusterTemplate struct {
@@ -76,7 +82,7 @@ func (c *SchedulerConfig) Default(_ *field.Path) error {
7682 c .Strategy = STRATEGY_BALANCED
7783 }
7884 if c .PurposeMappings == nil {
79- c .PurposeMappings = map [string ]ClusterDefinition {}
85+ c .PurposeMappings = map [string ]* ClusterDefinition {}
8086 }
8187 return nil
8288}
@@ -118,7 +124,11 @@ func (c *SchedulerConfig) Validate(fldPath *field.Path) error {
118124 for purpose , definition := range c .PurposeMappings {
119125 pPath := fldPath .Key (purpose )
120126 if purpose == "" {
121- errs = append (errs , field .Invalid (pPath , purpose , "purpose must not be empty" ))
127+ errs = append (errs , field .Invalid (fldPath , purpose , "purpose must not be empty" ))
128+ }
129+ if definition == nil {
130+ errs = append (errs , field .Required (pPath , "definition must not be nil" ))
131+ continue
122132 }
123133 if definition .TenancyCount < 0 {
124134 errs = append (errs , field .Invalid (pPath .Child ("tenancyCount" ), definition .TenancyCount , "tenancyCount must be greater than or equal to 0" ))
@@ -137,7 +147,18 @@ func (c *SchedulerConfig) Validate(fldPath *field.Path) error {
137147 errs = append (errs , field .Invalid (pPath .Child ("tenancyCount" ), definition .TenancyCount , fmt .Sprintf ("tenancyCount must be 0 if the template specifies '%s' tenancy" , string (clustersv1alpha1 .TENANCY_EXCLUSIVE ))))
138148 }
139149 if cls != nil && ! cls .Matches (labels .Set (definition .Template .Labels )) {
140- errs = append (errs , field .Invalid (pPath .Child ("template" ).Child ("metadata" ).Child ("labels" ), definition .Template .Labels , "labels do not match specified cluster selector" ))
150+ errs = append (errs , field .Invalid (pPath .Child ("template" ).Child ("metadata" ).Child ("labels" ), definition .Template .Labels , "labels do not match specified global cluster selector" ))
151+ }
152+ var lcls labels.Selector
153+ if definition .Selector != nil {
154+ var err error
155+ lcls , err = metav1 .LabelSelectorAsSelector (definition .Selector )
156+ if err != nil {
157+ errs = append (errs , field .Invalid (pPath .Child ("selector" ), definition .Selector , err .Error ()))
158+ }
159+ }
160+ if lcls != nil && ! lcls .Matches (labels .Set (definition .Template .Labels )) {
161+ errs = append (errs , field .Invalid (pPath .Child ("template" ).Child ("metadata" ).Child ("labels" ), definition .Template .Labels , "labels do not match specified local cluster selector" ))
141162 }
142163 }
143164 return errs .ToAggregate ()
@@ -166,5 +187,37 @@ func (c *SchedulerConfig) Complete(fldPath *field.Path) error {
166187 if c .CompletedSelectors .ClusterRequests == nil {
167188 c .CompletedSelectors .ClusterRequests = labels .Everything ()
168189 }
190+
191+ for purpose , definition := range c .PurposeMappings {
192+ pPath := fldPath .Child ("purposeMappings" ).Key (purpose )
193+ if definition .Selector != nil {
194+ var combinedSelector * metav1.LabelSelector
195+ if c .Selectors .Clusters == nil {
196+ combinedSelector = definition .Selector
197+ } else if definition .Selector == nil {
198+ combinedSelector = c .Selectors .Clusters
199+ } else {
200+ combinedSelector = c .Selectors .Clusters .DeepCopy ()
201+ if combinedSelector .MatchLabels == nil {
202+ combinedSelector .MatchLabels = definition .Selector .MatchLabels
203+ } else if definition .Selector .MatchLabels != nil {
204+ maps .Insert (combinedSelector .MatchLabels , maps .All (definition .Selector .MatchLabels ))
205+ }
206+ if combinedSelector .MatchExpressions == nil {
207+ combinedSelector .MatchExpressions = definition .Selector .MatchExpressions
208+ } else if definition .Selector .MatchExpressions != nil {
209+ combinedSelector .MatchExpressions = append (combinedSelector .MatchExpressions , definition .Selector .MatchExpressions ... )
210+ }
211+ }
212+ var err error
213+ definition .CompletedSelector , err = metav1 .LabelSelectorAsSelector (combinedSelector )
214+ if err != nil {
215+ return field .Invalid (pPath .Child ("selector" ), combinedSelector , fmt .Sprintf ("the combination of the global and local selector is invalid: %s" , err .Error ()))
216+ }
217+ } else {
218+ definition .CompletedSelector = c .CompletedSelectors .Clusters
219+ }
220+ }
221+
169222 return nil
170223}
0 commit comments