@@ -18,31 +18,43 @@ type rule struct {
1818}
1919
2020// Validate validates config values against x-deckhouse-validation rules in schema
21- func Validate (schema * spec.Schema , values map [string ]interface {}) error {
22- env , err := cel .NewEnv (cel .Variable ("self" , cel .MapType (cel .StringType , cel .DynType )))
23- if err != nil {
24- return fmt .Errorf ("create CEL env: %w" , err )
21+ func Validate (schema * spec.Schema , values any ) ([]error , error ) {
22+ var validationErrs []error
23+ // First we validate only object nested properties
24+ if m , ok := values .(map [string ]any ); ok {
25+ for propName , propSchema := range schema .Properties {
26+ if propValue , ok := m [propName ]; ok {
27+ subErrs , err := Validate (& propSchema , propValue )
28+ if err != nil {
29+ return nil , err
30+ }
31+ validationErrs = append (validationErrs , subErrs ... )
32+ }
33+ }
2534 }
2635
2736 raw , found := schema .Extensions [ruleKey ]
2837 if ! found {
29- return nil
38+ if len (validationErrs ) > 0 {
39+ return validationErrs , nil
40+ }
41+ return nil , nil
3042 }
3143
3244 var rules []rule
3345 switch v := raw .(type ) {
34- case []interface {} :
46+ case []any :
3547 for _ , entry := range v {
36- mapEntry , ok := entry .(map [string ]interface {} )
48+ mapEntry , ok := entry .(map [string ]any )
3749 if ! ok || len (mapEntry ) == 0 {
38- return fmt .Errorf ("x-deckhouse-validations invalid" )
50+ return nil , fmt .Errorf ("x-deckhouse-validations invalid" )
3951 }
4052
4153 if val , ok := mapEntry ["expression" ]; ! ok || len (val .(string )) == 0 {
42- return fmt .Errorf ("x-deckhouse-validations invalid: missing expression" )
54+ return nil , fmt .Errorf ("x-deckhouse-validations invalid: missing expression" )
4355 }
4456 if val , ok := mapEntry ["message" ]; ! ok || len (val .(string )) == 0 {
45- return fmt .Errorf ("x-deckhouse-validations invalid: missing message" )
57+ return nil , fmt .Errorf ("x-deckhouse-validations invalid: missing message" )
4658 }
4759
4860 rules = append (rules , rule {
@@ -51,41 +63,68 @@ func Validate(schema *spec.Schema, values map[string]interface{}) error {
5163 })
5264 }
5365 default :
54- return fmt .Errorf ("x-deckhouse-validations invalid" )
66+ return nil , fmt .Errorf ("x-deckhouse-validations invalid" )
5567 }
5668
57- obj , err := structpb . NewStruct (values )
69+ celSelfType , celSelfValue , err := buildCELValueAndType (values )
5870 if err != nil {
59- return fmt . Errorf ( "convert values to struct: %w" , err )
71+ return nil , err
6072 }
6173
74+ env , err := cel .NewEnv (cel .Variable ("self" , celSelfType ))
75+ if err != nil {
76+ return nil , fmt .Errorf ("create CEL env: %w" , err )
77+ }
6278 for _ , r := range rules {
6379 ast , issues := env .Compile (r .Expression )
6480 if issues .Err () != nil {
65- return fmt .Errorf ("compile the '%s' rule: %w" , r .Expression , issues .Err ())
81+ return nil , fmt .Errorf ("compile the '%s' rule: %w" , r .Expression , issues .Err ())
6682 }
6783
6884 prg , err := env .Program (ast )
6985 if err != nil {
70- return fmt .Errorf ("create program for the '%s' rule: %w" , r .Expression , err )
86+ return nil , fmt .Errorf ("create program for the '%s' rule: %w" , r .Expression , err )
7187 }
7288
73- out , _ , err := prg .Eval (map [string ]interface {}{ "self" : obj })
89+ out , _ , err := prg .Eval (map [string ]any { "self" : celSelfValue })
7490 if err != nil {
7591 if strings .Contains (err .Error (), "no such key:" ) {
7692 continue
7793 }
78- return fmt .Errorf ("evaluate the '%s' rule: %w" , r .Expression , err )
94+ return nil , fmt .Errorf ("evaluate the '%s' rule: %w" , r .Expression , err )
7995 }
8096
8197 pass , ok := out .Value ().(bool )
8298 if ! ok {
83- return errors .New ("rule should return boolean" )
99+ return nil , errors .New ("rule should return boolean" )
84100 }
85101 if ! pass {
86- return errors .New (r .Message )
102+ validationErrs = append ( validationErrs , errors .New (r .Message ) )
87103 }
88104 }
89105
90- return nil
106+ return validationErrs , nil
107+ }
108+
109+ func buildCELValueAndType (value any ) (* cel.Type , any , error ) {
110+ switch v := value .(type ) {
111+ case map [string ]any :
112+ obj , err := structpb .NewStruct (v )
113+ if err != nil {
114+ return nil , nil , fmt .Errorf ("convert values to struct: %w" , err )
115+ }
116+ return cel .MapType (cel .StringType , cel .DynType ), obj , nil
117+ case []any :
118+ list , err := structpb .NewList (v )
119+ if err != nil {
120+ return nil , nil , fmt .Errorf ("convert array to list: %w" , err )
121+ }
122+ return cel .ListType (cel .DynType ), list , nil
123+ default :
124+ val , err := structpb .NewValue (v )
125+ if err != nil {
126+ return nil , nil , fmt .Errorf ("convert dyn to value: %w" , err )
127+ }
128+ return cel .DynType , val , nil
129+ }
91130}
0 commit comments