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