77 "context"
88 "encoding/json"
99 "fmt"
10+ "reflect"
1011 "strings"
1112
1213 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
@@ -24,7 +25,7 @@ import (
2425// See: https://github.com/kubernetes-sigs/cluster-api/blob/v1.5.1/internal/topology/variables/cluster_variable_validation.go#L118
2526//
2627//nolint:lll // Adding for URL above, does not work when adding to end of line in a comment block.
27- func ValidateClusterVariable (
28+ func ValidateClusterVariable [ T any ] (
2829 value * clusterv1.ClusterVariable ,
2930 definition * clusterv1.ClusterClassVariable ,
3031 fldPath * field.Path ,
@@ -34,7 +35,7 @@ func ValidateClusterVariable(
3435 return field.ErrorList {err }
3536 }
3637
37- variableValue , err := unmarshalAndDefaultVariableValue (fldPath , value , structuralSchema )
38+ variableValue , err := unmarshalAndDefaultVariableValue [ T ] (fldPath , value , structuralSchema )
3839 if err != nil {
3940 return field.ErrorList {err }
4041 }
@@ -45,26 +46,27 @@ func ValidateClusterVariable(
4546 return err
4647 }
4748
49+ var oldVariableValue T
4850 // Validate variable against the schema using CEL.
49- if err := validateCEL (fldPath , variableValue , nil , structuralSchema ); err != nil {
51+ if err := validateCEL [ T ] (fldPath , variableValue , oldVariableValue , structuralSchema ); err != nil {
5052 return err
5153 }
5254
5355 return validateUnknownFields (fldPath , value , variableValue , apiExtensionsSchema )
5456}
5557
56- func unmarshalAndDefaultVariableValue (
58+ func unmarshalAndDefaultVariableValue [ T any ] (
5759 fldPath * field.Path ,
5860 value * clusterv1.ClusterVariable ,
5961 s * structuralschema.Structural ,
60- ) (any , * field.Error ) {
62+ ) (T , * field.Error ) {
6163 // Parse JSON value.
62- var variableValue any
64+ var variableValue T
6365 // Only try to unmarshal the clusterVariable if it is not nil, otherwise the variableValue is nil.
6466 // Note: A clusterVariable with a nil value is the result of setting the variable value to "null" via YAML.
6567 if value .Value .Raw != nil {
6668 if err := json .Unmarshal (value .Value .Raw , & variableValue ); err != nil {
67- return nil , field .Invalid (
69+ return variableValue , field .Invalid (
6870 fldPath .Child ("value" ), string (value .Value .Raw ),
6971 fmt .Sprintf ("variable %q could not be parsed: %v" , value .Name , err ),
7072 )
@@ -122,9 +124,9 @@ func validatorAndSchemas(
122124 return validator , apiExtensionsSchema , s , nil
123125}
124126
125- func validateCEL (
127+ func validateCEL [ T any ] (
126128 fldPath * field.Path ,
127- variableValue , oldVariableValue any ,
129+ variableValue , oldVariableValue T ,
128130 structuralSchema * structuralschema.Structural ,
129131) field.ErrorList {
130132 // Note: k/k CR validation also uses celconfig.PerCallLimit when creating the validator for a custom resource.
@@ -138,12 +140,13 @@ func validateCEL(
138140
139141 // Note: k/k CRD validation also uses celconfig.RuntimeCELCostBudget for the Validate call.
140142 // The current RuntimeCELCostBudget gives roughly 1 second for the validation of a variable value.
141- if validationErrors , _ := celValidator . Validate (
143+ if validationErrors := validateCELRecursively (
142144 context .Background (),
145+ celValidator ,
143146 fldPath .Child ("value" ),
144147 structuralSchema ,
145- variableValue ,
146- oldVariableValue ,
148+ reflect . ValueOf ( variableValue ) ,
149+ reflect . ValueOf ( oldVariableValue ) ,
147150 celconfig .RuntimeCELCostBudget ,
148151 ); len (validationErrors ) > 0 {
149152 var allErrs field.ErrorList
@@ -215,8 +218,8 @@ func validateUnknownFields(
215218 return nil
216219}
217220
218- // ValidateClusterVariable validates an update to a clusterVariable.
219- func ValidateClusterVariableUpdate (
221+ // ValidateClusterVariableUpdate validates an update to a clusterVariable.
222+ func ValidateClusterVariableUpdate [ T any ] (
220223 value , oldValue * clusterv1.ClusterVariable ,
221224 definition * clusterv1.ClusterClassVariable ,
222225 fldPath * field.Path ,
@@ -226,12 +229,12 @@ func ValidateClusterVariableUpdate(
226229 return field.ErrorList {err }
227230 }
228231
229- variableValue , err := unmarshalAndDefaultVariableValue (fldPath , value , structuralSchema )
232+ variableValue , err := unmarshalAndDefaultVariableValue [ T ] (fldPath , value , structuralSchema )
230233 if err != nil {
231234 return field.ErrorList {err }
232235 }
233236
234- oldVariableValue , err := unmarshalAndDefaultVariableValue (fldPath , oldValue , structuralSchema )
237+ oldVariableValue , err := unmarshalAndDefaultVariableValue [ T ] (fldPath , oldValue , structuralSchema )
235238 if err != nil {
236239 return field.ErrorList {err }
237240 }
@@ -243,7 +246,7 @@ func ValidateClusterVariableUpdate(
243246 }
244247
245248 // Validate variable against the schema using CEL.
246- if err := validateCEL (fldPath , variableValue , oldVariableValue , structuralSchema ); err != nil {
249+ if err := validateCEL [ T ] (fldPath , variableValue , oldVariableValue , structuralSchema ); err != nil {
247250 return err
248251 }
249252
0 commit comments