k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/schemamutation/walker.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package schemamutation 18 19 import ( 20 "k8s.io/kube-openapi/pkg/validation/spec" 21 ) 22 23 // Walker runs callback functions on all references of an OpenAPI spec, 24 // replacing the values when visiting corresponding types. 25 type Walker struct { 26 // SchemaCallback will be called on each schema, taking the original schema, 27 // and before any other callbacks of the Walker. 28 // If the schema needs to be mutated, DO NOT mutate it in-place, 29 // always create a copy, mutate, and return it. 30 SchemaCallback func(schema *spec.Schema) *spec.Schema 31 32 // RefCallback will be called on each ref. 33 // If the ref needs to be mutated, DO NOT mutate it in-place, 34 // always create a copy, mutate, and return it. 35 RefCallback func(ref *spec.Ref) *spec.Ref 36 } 37 38 type SchemaCallbackFunc func(schema *spec.Schema) *spec.Schema 39 type RefCallbackFunc func(ref *spec.Ref) *spec.Ref 40 41 var SchemaCallBackNoop SchemaCallbackFunc = func(schema *spec.Schema) *spec.Schema { 42 return schema 43 } 44 var RefCallbackNoop RefCallbackFunc = func(ref *spec.Ref) *spec.Ref { 45 return ref 46 } 47 48 // ReplaceReferences rewrites the references without mutating the input. 49 // The output might share data with the input. 50 func ReplaceReferences(walkRef func(ref *spec.Ref) *spec.Ref, sp *spec.Swagger) *spec.Swagger { 51 walker := &Walker{RefCallback: walkRef, SchemaCallback: SchemaCallBackNoop} 52 return walker.WalkRoot(sp) 53 } 54 55 func (w *Walker) WalkSchema(schema *spec.Schema) *spec.Schema { 56 if schema == nil { 57 return nil 58 } 59 60 orig := schema 61 clone := func() { 62 if orig == schema { 63 schema = &spec.Schema{} 64 *schema = *orig 65 } 66 } 67 68 // Always run callback on the whole schema first 69 // so that SchemaCallback can take the original schema as input. 70 schema = w.SchemaCallback(schema) 71 72 if r := w.RefCallback(&schema.Ref); r != &schema.Ref { 73 clone() 74 schema.Ref = *r 75 } 76 77 definitionsCloned := false 78 for k, v := range schema.Definitions { 79 if s := w.WalkSchema(&v); s != &v { 80 if !definitionsCloned { 81 definitionsCloned = true 82 clone() 83 schema.Definitions = make(spec.Definitions, len(orig.Definitions)) 84 for k2, v2 := range orig.Definitions { 85 schema.Definitions[k2] = v2 86 } 87 } 88 schema.Definitions[k] = *s 89 } 90 } 91 92 propertiesCloned := false 93 for k, v := range schema.Properties { 94 if s := w.WalkSchema(&v); s != &v { 95 if !propertiesCloned { 96 propertiesCloned = true 97 clone() 98 schema.Properties = make(map[string]spec.Schema, len(orig.Properties)) 99 for k2, v2 := range orig.Properties { 100 schema.Properties[k2] = v2 101 } 102 } 103 schema.Properties[k] = *s 104 } 105 } 106 107 patternPropertiesCloned := false 108 for k, v := range schema.PatternProperties { 109 if s := w.WalkSchema(&v); s != &v { 110 if !patternPropertiesCloned { 111 patternPropertiesCloned = true 112 clone() 113 schema.PatternProperties = make(map[string]spec.Schema, len(orig.PatternProperties)) 114 for k2, v2 := range orig.PatternProperties { 115 schema.PatternProperties[k2] = v2 116 } 117 } 118 schema.PatternProperties[k] = *s 119 } 120 } 121 122 allOfCloned := false 123 for i := range schema.AllOf { 124 if s := w.WalkSchema(&schema.AllOf[i]); s != &schema.AllOf[i] { 125 if !allOfCloned { 126 allOfCloned = true 127 clone() 128 schema.AllOf = make([]spec.Schema, len(orig.AllOf)) 129 copy(schema.AllOf, orig.AllOf) 130 } 131 schema.AllOf[i] = *s 132 } 133 } 134 135 anyOfCloned := false 136 for i := range schema.AnyOf { 137 if s := w.WalkSchema(&schema.AnyOf[i]); s != &schema.AnyOf[i] { 138 if !anyOfCloned { 139 anyOfCloned = true 140 clone() 141 schema.AnyOf = make([]spec.Schema, len(orig.AnyOf)) 142 copy(schema.AnyOf, orig.AnyOf) 143 } 144 schema.AnyOf[i] = *s 145 } 146 } 147 148 oneOfCloned := false 149 for i := range schema.OneOf { 150 if s := w.WalkSchema(&schema.OneOf[i]); s != &schema.OneOf[i] { 151 if !oneOfCloned { 152 oneOfCloned = true 153 clone() 154 schema.OneOf = make([]spec.Schema, len(orig.OneOf)) 155 copy(schema.OneOf, orig.OneOf) 156 } 157 schema.OneOf[i] = *s 158 } 159 } 160 161 if schema.Not != nil { 162 if s := w.WalkSchema(schema.Not); s != schema.Not { 163 clone() 164 schema.Not = s 165 } 166 } 167 168 if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil { 169 if s := w.WalkSchema(schema.AdditionalProperties.Schema); s != schema.AdditionalProperties.Schema { 170 clone() 171 schema.AdditionalProperties = &spec.SchemaOrBool{Schema: s, Allows: schema.AdditionalProperties.Allows} 172 } 173 } 174 175 if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil { 176 if s := w.WalkSchema(schema.AdditionalItems.Schema); s != schema.AdditionalItems.Schema { 177 clone() 178 schema.AdditionalItems = &spec.SchemaOrBool{Schema: s, Allows: schema.AdditionalItems.Allows} 179 } 180 } 181 182 if schema.Items != nil { 183 if schema.Items.Schema != nil { 184 if s := w.WalkSchema(schema.Items.Schema); s != schema.Items.Schema { 185 clone() 186 schema.Items = &spec.SchemaOrArray{Schema: s} 187 } 188 } else { 189 itemsCloned := false 190 for i := range schema.Items.Schemas { 191 if s := w.WalkSchema(&schema.Items.Schemas[i]); s != &schema.Items.Schemas[i] { 192 if !itemsCloned { 193 clone() 194 schema.Items = &spec.SchemaOrArray{ 195 Schemas: make([]spec.Schema, len(orig.Items.Schemas)), 196 } 197 itemsCloned = true 198 copy(schema.Items.Schemas, orig.Items.Schemas) 199 } 200 schema.Items.Schemas[i] = *s 201 } 202 } 203 } 204 } 205 206 return schema 207 } 208 209 func (w *Walker) walkParameter(param *spec.Parameter) *spec.Parameter { 210 if param == nil { 211 return nil 212 } 213 214 orig := param 215 cloned := false 216 clone := func() { 217 if !cloned { 218 cloned = true 219 param = &spec.Parameter{} 220 *param = *orig 221 } 222 } 223 224 if r := w.RefCallback(¶m.Ref); r != ¶m.Ref { 225 clone() 226 param.Ref = *r 227 } 228 if s := w.WalkSchema(param.Schema); s != param.Schema { 229 clone() 230 param.Schema = s 231 } 232 if param.Items != nil { 233 if r := w.RefCallback(¶m.Items.Ref); r != ¶m.Items.Ref { 234 param.Items.Ref = *r 235 } 236 } 237 238 return param 239 } 240 241 func (w *Walker) walkParameters(params []spec.Parameter) ([]spec.Parameter, bool) { 242 if params == nil { 243 return nil, false 244 } 245 246 orig := params 247 cloned := false 248 clone := func() { 249 if !cloned { 250 cloned = true 251 params = make([]spec.Parameter, len(params)) 252 copy(params, orig) 253 } 254 } 255 256 for i := range params { 257 if s := w.walkParameter(¶ms[i]); s != ¶ms[i] { 258 clone() 259 params[i] = *s 260 } 261 } 262 263 return params, cloned 264 } 265 266 func (w *Walker) walkResponse(resp *spec.Response) *spec.Response { 267 if resp == nil { 268 return nil 269 } 270 271 orig := resp 272 cloned := false 273 clone := func() { 274 if !cloned { 275 cloned = true 276 resp = &spec.Response{} 277 *resp = *orig 278 } 279 } 280 281 if r := w.RefCallback(&resp.Ref); r != &resp.Ref { 282 clone() 283 resp.Ref = *r 284 } 285 if s := w.WalkSchema(resp.Schema); s != resp.Schema { 286 clone() 287 resp.Schema = s 288 } 289 290 return resp 291 } 292 293 func (w *Walker) walkResponses(resps *spec.Responses) *spec.Responses { 294 if resps == nil { 295 return nil 296 } 297 298 orig := resps 299 cloned := false 300 clone := func() { 301 if !cloned { 302 cloned = true 303 resps = &spec.Responses{} 304 *resps = *orig 305 } 306 } 307 308 if r := w.walkResponse(resps.ResponsesProps.Default); r != resps.ResponsesProps.Default { 309 clone() 310 resps.Default = r 311 } 312 313 responsesCloned := false 314 for k, v := range resps.ResponsesProps.StatusCodeResponses { 315 if r := w.walkResponse(&v); r != &v { 316 if !responsesCloned { 317 responsesCloned = true 318 clone() 319 resps.ResponsesProps.StatusCodeResponses = make(map[int]spec.Response, len(orig.StatusCodeResponses)) 320 for k2, v2 := range orig.StatusCodeResponses { 321 resps.ResponsesProps.StatusCodeResponses[k2] = v2 322 } 323 } 324 resps.ResponsesProps.StatusCodeResponses[k] = *r 325 } 326 } 327 328 return resps 329 } 330 331 func (w *Walker) walkOperation(op *spec.Operation) *spec.Operation { 332 if op == nil { 333 return nil 334 } 335 336 orig := op 337 cloned := false 338 clone := func() { 339 if !cloned { 340 cloned = true 341 op = &spec.Operation{} 342 *op = *orig 343 } 344 } 345 346 parametersCloned := false 347 for i := range op.Parameters { 348 if s := w.walkParameter(&op.Parameters[i]); s != &op.Parameters[i] { 349 if !parametersCloned { 350 parametersCloned = true 351 clone() 352 op.Parameters = make([]spec.Parameter, len(orig.Parameters)) 353 copy(op.Parameters, orig.Parameters) 354 } 355 op.Parameters[i] = *s 356 } 357 } 358 359 if r := w.walkResponses(op.Responses); r != op.Responses { 360 clone() 361 op.Responses = r 362 } 363 364 return op 365 } 366 367 func (w *Walker) walkPathItem(pathItem *spec.PathItem) *spec.PathItem { 368 if pathItem == nil { 369 return nil 370 } 371 372 orig := pathItem 373 cloned := false 374 clone := func() { 375 if !cloned { 376 cloned = true 377 pathItem = &spec.PathItem{} 378 *pathItem = *orig 379 } 380 } 381 382 if p, changed := w.walkParameters(pathItem.Parameters); changed { 383 clone() 384 pathItem.Parameters = p 385 } 386 if op := w.walkOperation(pathItem.Get); op != pathItem.Get { 387 clone() 388 pathItem.Get = op 389 } 390 if op := w.walkOperation(pathItem.Head); op != pathItem.Head { 391 clone() 392 pathItem.Head = op 393 } 394 if op := w.walkOperation(pathItem.Delete); op != pathItem.Delete { 395 clone() 396 pathItem.Delete = op 397 } 398 if op := w.walkOperation(pathItem.Options); op != pathItem.Options { 399 clone() 400 pathItem.Options = op 401 } 402 if op := w.walkOperation(pathItem.Patch); op != pathItem.Patch { 403 clone() 404 pathItem.Patch = op 405 } 406 if op := w.walkOperation(pathItem.Post); op != pathItem.Post { 407 clone() 408 pathItem.Post = op 409 } 410 if op := w.walkOperation(pathItem.Put); op != pathItem.Put { 411 clone() 412 pathItem.Put = op 413 } 414 415 return pathItem 416 } 417 418 func (w *Walker) walkPaths(paths *spec.Paths) *spec.Paths { 419 if paths == nil { 420 return nil 421 } 422 423 orig := paths 424 cloned := false 425 clone := func() { 426 if !cloned { 427 cloned = true 428 paths = &spec.Paths{} 429 *paths = *orig 430 } 431 } 432 433 pathsCloned := false 434 for k, v := range paths.Paths { 435 if p := w.walkPathItem(&v); p != &v { 436 if !pathsCloned { 437 pathsCloned = true 438 clone() 439 paths.Paths = make(map[string]spec.PathItem, len(orig.Paths)) 440 for k2, v2 := range orig.Paths { 441 paths.Paths[k2] = v2 442 } 443 } 444 paths.Paths[k] = *p 445 } 446 } 447 448 return paths 449 } 450 451 func (w *Walker) WalkRoot(swagger *spec.Swagger) *spec.Swagger { 452 if swagger == nil { 453 return nil 454 } 455 456 orig := swagger 457 cloned := false 458 clone := func() { 459 if !cloned { 460 cloned = true 461 swagger = &spec.Swagger{} 462 *swagger = *orig 463 } 464 } 465 466 parametersCloned := false 467 for k, v := range swagger.Parameters { 468 if p := w.walkParameter(&v); p != &v { 469 if !parametersCloned { 470 parametersCloned = true 471 clone() 472 swagger.Parameters = make(map[string]spec.Parameter, len(orig.Parameters)) 473 for k2, v2 := range orig.Parameters { 474 swagger.Parameters[k2] = v2 475 } 476 } 477 swagger.Parameters[k] = *p 478 } 479 } 480 481 responsesCloned := false 482 for k, v := range swagger.Responses { 483 if r := w.walkResponse(&v); r != &v { 484 if !responsesCloned { 485 responsesCloned = true 486 clone() 487 swagger.Responses = make(map[string]spec.Response, len(orig.Responses)) 488 for k2, v2 := range orig.Responses { 489 swagger.Responses[k2] = v2 490 } 491 } 492 swagger.Responses[k] = *r 493 } 494 } 495 496 definitionsCloned := false 497 for k, v := range swagger.Definitions { 498 if s := w.WalkSchema(&v); s != &v { 499 if !definitionsCloned { 500 definitionsCloned = true 501 clone() 502 swagger.Definitions = make(spec.Definitions, len(orig.Definitions)) 503 for k2, v2 := range orig.Definitions { 504 swagger.Definitions[k2] = v2 505 } 506 } 507 swagger.Definitions[k] = *s 508 } 509 } 510 511 if swagger.Paths != nil { 512 if p := w.walkPaths(swagger.Paths); p != swagger.Paths { 513 clone() 514 swagger.Paths = p 515 } 516 } 517 518 return swagger 519 }