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(&param.Ref); r != &param.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(&param.Items.Ref); r != &param.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(&params[i]); s != &params[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  }