...

Source file src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta/algorithm.go

Documentation: k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta

     1  /*
     2  Copyright 2019 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 objectmeta
    18  
    19  import (
    20  	"sort"
    21  
    22  	structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
    23  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    24  	"k8s.io/apimachinery/pkg/util/validation/field"
    25  )
    26  
    27  // CoerceOptions gives the ability to ReturnUnknownFieldPaths for fields
    28  // unrecognized by the schema or DropInvalidFields for fields that are a part
    29  // of the schema, but are malformed.
    30  type CoerceOptions struct {
    31  	// DropInvalidFields discards malformed serialized metadata fields that
    32  	// cannot be successfully decoded to the corresponding ObjectMeta field.
    33  	// This only applies to fields that are recognized as part of the schema,
    34  	// but of an invalid type (i.e. cause an error when unmarshaling, rather
    35  	// than being dropped or causing a strictErr).
    36  	DropInvalidFields bool
    37  	// ReturnUnknownFieldPaths will return the paths to fields that are not
    38  	// recognized as part of the schema.
    39  	ReturnUnknownFieldPaths bool
    40  }
    41  
    42  // Coerce checks types of embedded ObjectMeta and TypeMeta and prunes unknown fields inside the former.
    43  // It does coerce ObjectMeta and TypeMeta at the root if isResourceRoot is true.
    44  // If opts.ReturnUnknownFieldPaths is true, it will return the paths of any fields that are not a part of the
    45  // schema that are dropped when unmarshaling.
    46  // If opts.DropInvalidFields is true, fields of wrong type will be dropped.
    47  func CoerceWithOptions(pth *field.Path, obj interface{}, s *structuralschema.Structural, isResourceRoot bool, opts CoerceOptions) (*field.Error, []string) {
    48  	if isResourceRoot {
    49  		if s == nil {
    50  			s = &structuralschema.Structural{}
    51  		}
    52  		if !s.XEmbeddedResource {
    53  			clone := *s
    54  			clone.XEmbeddedResource = true
    55  			s = &clone
    56  		}
    57  	}
    58  	c := coercer{DropInvalidFields: opts.DropInvalidFields, ReturnUnknownFieldPaths: opts.ReturnUnknownFieldPaths}
    59  	schemaOpts := &structuralschema.UnknownFieldPathOptions{
    60  		TrackUnknownFieldPaths: opts.ReturnUnknownFieldPaths,
    61  	}
    62  	fieldErr := c.coerce(pth, obj, s, schemaOpts)
    63  	sort.Strings(schemaOpts.UnknownFieldPaths)
    64  	return fieldErr, schemaOpts.UnknownFieldPaths
    65  }
    66  
    67  // Coerce calls CoerceWithOptions without returning unknown field paths.
    68  func Coerce(pth *field.Path, obj interface{}, s *structuralschema.Structural, isResourceRoot, dropInvalidFields bool) *field.Error {
    69  	fieldErr, _ := CoerceWithOptions(pth, obj, s, isResourceRoot, CoerceOptions{DropInvalidFields: dropInvalidFields})
    70  	return fieldErr
    71  }
    72  
    73  type coercer struct {
    74  	DropInvalidFields       bool
    75  	ReturnUnknownFieldPaths bool
    76  }
    77  
    78  func (c *coercer) coerce(pth *field.Path, x interface{}, s *structuralschema.Structural, opts *structuralschema.UnknownFieldPathOptions) *field.Error {
    79  	if s == nil {
    80  		return nil
    81  	}
    82  	origPathLen := len(opts.ParentPath)
    83  	defer func() {
    84  		opts.ParentPath = opts.ParentPath[:origPathLen]
    85  	}()
    86  	switch x := x.(type) {
    87  	case map[string]interface{}:
    88  		for k, v := range x {
    89  			if s.XEmbeddedResource {
    90  				switch k {
    91  				case "apiVersion", "kind":
    92  					if _, ok := v.(string); !ok && c.DropInvalidFields {
    93  						delete(x, k)
    94  					} else if !ok {
    95  						return field.Invalid(pth.Child(k), v, "must be a string")
    96  					}
    97  				case "metadata":
    98  					meta, found, unknownFields, err := GetObjectMetaWithOptions(x, ObjectMetaOptions{
    99  						DropMalformedFields:     c.DropInvalidFields,
   100  						ReturnUnknownFieldPaths: c.ReturnUnknownFieldPaths,
   101  						ParentPath:              pth,
   102  					})
   103  					opts.UnknownFieldPaths = append(opts.UnknownFieldPaths, unknownFields...)
   104  					if err != nil {
   105  						if !c.DropInvalidFields {
   106  							return field.Invalid(pth.Child("metadata"), v, err.Error())
   107  						}
   108  						// pass through on error if DropInvalidFields is true
   109  					} else if found {
   110  						if err := SetObjectMeta(x, meta); err != nil {
   111  							return field.Invalid(pth.Child("metadata"), v, err.Error())
   112  						}
   113  						if meta.CreationTimestamp.IsZero() {
   114  							unstructured.RemoveNestedField(x, "metadata", "creationTimestamp")
   115  						}
   116  					}
   117  				}
   118  			}
   119  			prop, ok := s.Properties[k]
   120  			if ok {
   121  				opts.AppendKey(k)
   122  				if err := c.coerce(pth.Child(k), v, &prop, opts); err != nil {
   123  					return err
   124  				}
   125  				opts.ParentPath = opts.ParentPath[:origPathLen]
   126  			} else if s.AdditionalProperties != nil {
   127  				opts.AppendKey(k)
   128  				if err := c.coerce(pth.Key(k), v, s.AdditionalProperties.Structural, opts); err != nil {
   129  					return err
   130  				}
   131  				opts.ParentPath = opts.ParentPath[:origPathLen]
   132  			}
   133  		}
   134  	case []interface{}:
   135  		for i, v := range x {
   136  			opts.AppendIndex(i)
   137  			if err := c.coerce(pth.Index(i), v, s.Items, opts); err != nil {
   138  				return err
   139  			}
   140  			opts.ParentPath = opts.ParentPath[:origPathLen]
   141  		}
   142  	default:
   143  		// scalars, do nothing
   144  	}
   145  
   146  	return nil
   147  }
   148  

View as plain text