@@ -123,8 +123,16 @@ const Env = z
123123 KUBERNETES_SCHEDULER_NAME : z . string ( ) . optional ( ) , // Custom scheduler name for pods
124124 // Large machine affinity settings - large-* presets prefer a dedicated pool
125125 KUBERNETES_LARGE_MACHINE_AFFINITY_ENABLED : BoolEnv . default ( false ) ,
126- KUBERNETES_LARGE_MACHINE_AFFINITY_POOL_LABEL_KEY : z . string ( ) . trim ( ) . min ( 1 ) . default ( "node.cluster.x-k8s.io/machinepool" ) ,
127- KUBERNETES_LARGE_MACHINE_AFFINITY_POOL_LABEL_VALUE : z . string ( ) . trim ( ) . min ( 1 ) . default ( "large-machines" ) ,
126+ KUBERNETES_LARGE_MACHINE_AFFINITY_POOL_LABEL_KEY : z
127+ . string ( )
128+ . trim ( )
129+ . min ( 1 )
130+ . default ( "node.cluster.x-k8s.io/machinepool" ) ,
131+ KUBERNETES_LARGE_MACHINE_AFFINITY_POOL_LABEL_VALUE : z
132+ . string ( )
133+ . trim ( )
134+ . min ( 1 )
135+ . default ( "large-machines" ) ,
128136 KUBERNETES_LARGE_MACHINE_AFFINITY_WEIGHT : z . coerce . number ( ) . int ( ) . min ( 1 ) . max ( 100 ) . default ( 100 ) ,
129137
130138 // Project affinity settings - pods from the same project prefer the same node
@@ -137,11 +145,82 @@ const Env = z
137145 . default ( "kubernetes.io/hostname" ) ,
138146
139147 // Schedule affinity settings - runs from schedule trees prefer a dedicated pool
140- KUBERNETES_SCHEDULE_AFFINITY_ENABLED : BoolEnv . default ( false ) ,
141- KUBERNETES_SCHEDULE_AFFINITY_POOL_LABEL_KEY : z . string ( ) . trim ( ) . min ( 1 ) . default ( "node.cluster.x-k8s.io/machinepool" ) ,
142- KUBERNETES_SCHEDULE_AFFINITY_POOL_LABEL_VALUE : z . string ( ) . trim ( ) . min ( 1 ) . default ( "scheduled-runs" ) ,
143- KUBERNETES_SCHEDULE_AFFINITY_WEIGHT : z . coerce . number ( ) . int ( ) . min ( 1 ) . max ( 100 ) . default ( 80 ) ,
144- KUBERNETES_SCHEDULE_ANTI_AFFINITY_WEIGHT : z . coerce . number ( ) . int ( ) . min ( 1 ) . max ( 100 ) . default ( 20 ) ,
148+ KUBERNETES_SCHEDULED_RUN_AFFINITY_ENABLED : BoolEnv . default ( false ) ,
149+ KUBERNETES_SCHEDULED_RUN_AFFINITY_POOL_LABEL_KEY : z
150+ . string ( )
151+ . trim ( )
152+ . min ( 1 )
153+ . default ( "node.cluster.x-k8s.io/machinepool" ) ,
154+ KUBERNETES_SCHEDULED_RUN_AFFINITY_POOL_LABEL_VALUE : z
155+ . string ( )
156+ . trim ( )
157+ . min ( 1 )
158+ . default ( "scheduled-runs" ) ,
159+ KUBERNETES_SCHEDULED_RUN_AFFINITY_WEIGHT : z . coerce . number ( ) . int ( ) . min ( 1 ) . max ( 100 ) . default ( 80 ) ,
160+ KUBERNETES_SCHEDULED_RUN_ANTI_AFFINITY_WEIGHT : z . coerce
161+ . number ( )
162+ . int ( )
163+ . min ( 1 )
164+ . max ( 100 )
165+ . default ( 20 ) ,
166+
167+ // Schedule toleration settings - scheduled runs tolerate taints on the dedicated pool
168+ // Comma-separated list of tolerations in the format: key=value:effect
169+ // For Exists operator (no value): key:effect
170+ KUBERNETES_SCHEDULED_RUN_TOLERATIONS : z
171+ . string ( )
172+ . transform ( ( val , ctx ) => {
173+ const tolerations = val
174+ . split ( "," )
175+ . map ( ( entry ) => entry . trim ( ) )
176+ . filter ( ( entry ) => entry . length > 0 )
177+ . map ( ( entry ) => {
178+ const colonIdx = entry . lastIndexOf ( ":" ) ;
179+ if ( colonIdx === - 1 ) {
180+ ctx . addIssue ( {
181+ code : z . ZodIssueCode . custom ,
182+ message : `Invalid toleration format (missing effect): "${ entry } "` ,
183+ } ) ;
184+ return z . NEVER ;
185+ }
186+
187+ const effect = entry . slice ( colonIdx + 1 ) ;
188+ const validEffects = [ "NoSchedule" , "NoExecute" , "PreferNoSchedule" ] ;
189+ if ( ! validEffects . includes ( effect ) ) {
190+ ctx . addIssue ( {
191+ code : z . ZodIssueCode . custom ,
192+ message : `Invalid toleration effect "${ effect } " in "${ entry } ". Must be one of: ${ validEffects . join ( ", " ) } ` ,
193+ } ) ;
194+ return z . NEVER ;
195+ }
196+
197+ const keyValue = entry . slice ( 0 , colonIdx ) ;
198+ const eqIdx = keyValue . indexOf ( "=" ) ;
199+ const key = eqIdx === - 1 ? keyValue : keyValue . slice ( 0 , eqIdx ) ;
200+
201+ if ( ! key ) {
202+ ctx . addIssue ( {
203+ code : z . ZodIssueCode . custom ,
204+ message : `Invalid toleration format (empty key): "${ entry } "` ,
205+ } ) ;
206+ return z . NEVER ;
207+ }
208+
209+ if ( eqIdx === - 1 ) {
210+ return { key, operator : "Exists" as const , effect } ;
211+ }
212+
213+ return {
214+ key,
215+ operator : "Equal" as const ,
216+ value : keyValue . slice ( eqIdx + 1 ) ,
217+ effect,
218+ } ;
219+ } ) ;
220+
221+ return tolerations ;
222+ } )
223+ . optional ( ) ,
145224
146225 // Placement tags settings
147226 PLACEMENT_TAGS_ENABLED : BoolEnv . default ( false ) ,
0 commit comments