k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/validation/validate/schema_props.go (about) 1 // Copyright 2015 go-swagger maintainers 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package validate 16 17 import ( 18 "fmt" 19 "reflect" 20 21 "k8s.io/kube-openapi/pkg/validation/spec" 22 "k8s.io/kube-openapi/pkg/validation/strfmt" 23 ) 24 25 type schemaPropsValidator struct { 26 Path string 27 In string 28 AllOf []spec.Schema 29 OneOf []spec.Schema 30 AnyOf []spec.Schema 31 Not *spec.Schema 32 Dependencies spec.Dependencies 33 anyOfValidators []SchemaValidator 34 allOfValidators []SchemaValidator 35 oneOfValidators []SchemaValidator 36 notValidator *SchemaValidator 37 Root interface{} 38 KnownFormats strfmt.Registry 39 Options SchemaValidatorOptions 40 } 41 42 func (s *schemaPropsValidator) SetPath(path string) { 43 s.Path = path 44 for _, v := range s.anyOfValidators { 45 v.SetPath(path) 46 } 47 for _, v := range s.allOfValidators { 48 v.SetPath(path) 49 } 50 for _, v := range s.oneOfValidators { 51 v.SetPath(path) 52 } 53 if s.notValidator != nil { 54 s.notValidator.SetPath(path) 55 } 56 } 57 58 func newSchemaPropsValidator(path string, in string, allOf, oneOf, anyOf []spec.Schema, not *spec.Schema, deps spec.Dependencies, root interface{}, formats strfmt.Registry, options ...Option) *schemaPropsValidator { 59 var anyValidators []SchemaValidator 60 for _, v := range anyOf { 61 v := v 62 anyValidators = append(anyValidators, *NewSchemaValidator(&v, root, path, formats, options...)) 63 } 64 var allValidators []SchemaValidator 65 for _, v := range allOf { 66 v := v 67 allValidators = append(allValidators, *NewSchemaValidator(&v, root, path, formats, options...)) 68 } 69 var oneValidators []SchemaValidator 70 for _, v := range oneOf { 71 v := v 72 oneValidators = append(oneValidators, *NewSchemaValidator(&v, root, path, formats, options...)) 73 } 74 75 var notValidator *SchemaValidator 76 if not != nil { 77 notValidator = NewSchemaValidator(not, root, path, formats, options...) 78 } 79 80 schOptions := &SchemaValidatorOptions{} 81 for _, o := range options { 82 o(schOptions) 83 } 84 return &schemaPropsValidator{ 85 Path: path, 86 In: in, 87 AllOf: allOf, 88 OneOf: oneOf, 89 AnyOf: anyOf, 90 Not: not, 91 Dependencies: deps, 92 anyOfValidators: anyValidators, 93 allOfValidators: allValidators, 94 oneOfValidators: oneValidators, 95 notValidator: notValidator, 96 Root: root, 97 KnownFormats: formats, 98 Options: *schOptions, 99 } 100 } 101 102 func (s *schemaPropsValidator) Applies(source interface{}, kind reflect.Kind) bool { 103 r := reflect.TypeOf(source) == specSchemaType 104 debugLog("schema props validator for %q applies %t for %T (kind: %v)\n", s.Path, r, source, kind) 105 return r 106 } 107 108 func (s *schemaPropsValidator) Validate(data interface{}) *Result { 109 mainResult := new(Result) 110 111 // Intermediary error results 112 113 // IMPORTANT! messages from underlying validators 114 keepResultAnyOf := new(Result) 115 keepResultOneOf := new(Result) 116 keepResultAllOf := new(Result) 117 118 // Validates at least one in anyOf schemas 119 var firstSuccess *Result 120 if len(s.anyOfValidators) > 0 { 121 var bestFailures *Result 122 succeededOnce := false 123 for _, anyOfSchema := range s.anyOfValidators { 124 result := anyOfSchema.Validate(data) 125 // We keep inner IMPORTANT! errors no matter what MatchCount tells us 126 keepResultAnyOf.Merge(result.keepRelevantErrors()) 127 if result.IsValid() { 128 bestFailures = nil 129 succeededOnce = true 130 if firstSuccess == nil { 131 firstSuccess = result 132 } 133 keepResultAnyOf = new(Result) 134 break 135 } 136 // MatchCount is used to select errors from the schema with most positive checks 137 if bestFailures == nil || result.MatchCount > bestFailures.MatchCount { 138 bestFailures = result 139 } 140 } 141 142 if !succeededOnce { 143 mainResult.AddErrors(mustValidateAtLeastOneSchemaMsg(s.Path)) 144 } 145 if bestFailures != nil { 146 mainResult.Merge(bestFailures) 147 } else if firstSuccess != nil { 148 mainResult.Merge(firstSuccess) 149 } 150 } 151 152 // Validates exactly one in oneOf schemas 153 if len(s.oneOfValidators) > 0 { 154 var bestFailures *Result 155 var firstSuccess *Result 156 validated := 0 157 158 for _, oneOfSchema := range s.oneOfValidators { 159 result := oneOfSchema.Validate(data) 160 // We keep inner IMPORTANT! errors no matter what MatchCount tells us 161 keepResultOneOf.Merge(result.keepRelevantErrors()) 162 if result.IsValid() { 163 validated++ 164 bestFailures = nil 165 if firstSuccess == nil { 166 firstSuccess = result 167 } 168 keepResultOneOf = new(Result) 169 continue 170 } 171 // MatchCount is used to select errors from the schema with most positive checks 172 if validated == 0 && (bestFailures == nil || result.MatchCount > bestFailures.MatchCount) { 173 bestFailures = result 174 } 175 } 176 177 if validated != 1 { 178 additionalMsg := "" 179 if validated == 0 { 180 additionalMsg = "Found none valid" 181 } else { 182 additionalMsg = fmt.Sprintf("Found %d valid alternatives", validated) 183 } 184 185 mainResult.AddErrors(mustValidateOnlyOneSchemaMsg(s.Path, additionalMsg)) 186 if bestFailures != nil { 187 mainResult.Merge(bestFailures) 188 } 189 } else if firstSuccess != nil { 190 mainResult.Merge(firstSuccess) 191 } 192 } 193 194 // Validates all of allOf schemas 195 if len(s.allOfValidators) > 0 { 196 validated := 0 197 198 for _, allOfSchema := range s.allOfValidators { 199 result := allOfSchema.Validate(data) 200 // We keep inner IMPORTANT! errors no matter what MatchCount tells us 201 keepResultAllOf.Merge(result.keepRelevantErrors()) 202 //keepResultAllOf.Merge(result) 203 if result.IsValid() { 204 validated++ 205 } 206 mainResult.Merge(result) 207 } 208 209 if validated != len(s.allOfValidators) { 210 additionalMsg := "" 211 if validated == 0 { 212 additionalMsg = ". None validated" 213 } 214 215 mainResult.AddErrors(mustValidateAllSchemasMsg(s.Path, additionalMsg)) 216 } 217 } 218 219 if s.notValidator != nil { 220 result := s.notValidator.Validate(data) 221 // We keep inner IMPORTANT! errors no matter what MatchCount tells us 222 if result.IsValid() { 223 mainResult.AddErrors(mustNotValidatechemaMsg(s.Path)) 224 } 225 } 226 227 if s.Dependencies != nil && len(s.Dependencies) > 0 && reflect.TypeOf(data).Kind() == reflect.Map { 228 val := data.(map[string]interface{}) 229 for key := range val { 230 if dep, ok := s.Dependencies[key]; ok { 231 232 if dep.Schema != nil { 233 mainResult.Merge(NewSchemaValidator(dep.Schema, s.Root, s.Path+"."+key, s.KnownFormats, s.Options.Options()...).Validate(data)) 234 continue 235 } 236 237 if len(dep.Property) > 0 { 238 for _, depKey := range dep.Property { 239 if _, ok := val[depKey]; !ok { 240 mainResult.AddErrors(hasADependencyMsg(s.Path, depKey)) 241 } 242 } 243 } 244 } 245 } 246 } 247 248 mainResult.Inc() 249 // In the end we retain best failures for schema validation 250 // plus, if any, composite errors which may explain special cases (tagged as IMPORTANT!). 251 return mainResult.Merge(keepResultAllOf, keepResultOneOf, keepResultAnyOf) 252 }