...

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

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

     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 schema
    18  
    19  import (
    20  	"fmt"
    21  	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
    22  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    23  )
    24  
    25  // NewStructural converts an OpenAPI v3 schema into a structural schema. A pre-validated JSONSchemaProps will
    26  // not fail on NewStructural. This means that we require that:
    27  //
    28  // - items is not an array of schemas
    29  // - the following fields are not set:
    30  //   - id
    31  //   - schema
    32  //   - $ref
    33  //   - patternProperties
    34  //   - dependencies
    35  //   - additionalItems
    36  //   - definitions.
    37  //
    38  // The follow fields are not preserved:
    39  // - externalDocs
    40  // - example.
    41  func NewStructural(s *apiextensions.JSONSchemaProps) (*Structural, error) {
    42  	if s == nil {
    43  		return nil, nil
    44  	}
    45  
    46  	if err := validateUnsupportedFields(s); err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	vv, err := newValueValidation(s)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	g, err := newGenerics(s)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	x, err := newExtensions(s)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	ss := &Structural{
    66  		Generic:         *g,
    67  		Extensions:      *x,
    68  		ValueValidation: vv,
    69  	}
    70  
    71  	if s.Items != nil {
    72  		if len(s.Items.JSONSchemas) > 0 {
    73  			// we validate that it is not an array
    74  			return nil, fmt.Errorf("OpenAPIV3Schema 'items' must be a schema, but is an array")
    75  		}
    76  		item, err := NewStructural(s.Items.Schema)
    77  		if err != nil {
    78  			return nil, err
    79  		}
    80  		ss.Items = item
    81  	}
    82  
    83  	if len(s.Properties) > 0 {
    84  		ss.Properties = make(map[string]Structural, len(s.Properties))
    85  		for k, x := range s.Properties {
    86  			fld, err := NewStructural(&x)
    87  			if err != nil {
    88  				return nil, err
    89  			}
    90  			ss.Properties[k] = *fld
    91  		}
    92  	}
    93  
    94  	return ss, nil
    95  }
    96  
    97  func newGenerics(s *apiextensions.JSONSchemaProps) (*Generic, error) {
    98  	if s == nil {
    99  		return nil, nil
   100  	}
   101  	g := &Generic{
   102  		Type:        s.Type,
   103  		Description: s.Description,
   104  		Title:       s.Title,
   105  		Nullable:    s.Nullable,
   106  	}
   107  	if s.Default != nil {
   108  		g.Default = JSON{interface{}(*s.Default)}
   109  	}
   110  
   111  	if s.AdditionalProperties != nil {
   112  		if s.AdditionalProperties.Schema != nil {
   113  			ss, err := NewStructural(s.AdditionalProperties.Schema)
   114  			if err != nil {
   115  				return nil, err
   116  			}
   117  			g.AdditionalProperties = &StructuralOrBool{Structural: ss, Bool: true}
   118  		} else {
   119  			g.AdditionalProperties = &StructuralOrBool{Bool: s.AdditionalProperties.Allows}
   120  		}
   121  	}
   122  
   123  	return g, nil
   124  }
   125  
   126  func newValueValidation(s *apiextensions.JSONSchemaProps) (*ValueValidation, error) {
   127  	if s == nil {
   128  		return nil, nil
   129  	}
   130  	not, err := newNestedValueValidation(s.Not)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  	v := &ValueValidation{
   135  		Format:           s.Format,
   136  		Maximum:          s.Maximum,
   137  		ExclusiveMaximum: s.ExclusiveMaximum,
   138  		Minimum:          s.Minimum,
   139  		ExclusiveMinimum: s.ExclusiveMinimum,
   140  		MaxLength:        s.MaxLength,
   141  		MinLength:        s.MinLength,
   142  		Pattern:          s.Pattern,
   143  		MaxItems:         s.MaxItems,
   144  		MinItems:         s.MinItems,
   145  		UniqueItems:      s.UniqueItems,
   146  		MultipleOf:       s.MultipleOf,
   147  		MaxProperties:    s.MaxProperties,
   148  		MinProperties:    s.MinProperties,
   149  		Required:         s.Required,
   150  		Not:              not,
   151  	}
   152  
   153  	for _, e := range s.Enum {
   154  		v.Enum = append(v.Enum, JSON{e})
   155  	}
   156  
   157  	for _, x := range s.AllOf {
   158  		clause, err := newNestedValueValidation(&x)
   159  		if err != nil {
   160  			return nil, err
   161  		}
   162  		v.AllOf = append(v.AllOf, *clause)
   163  	}
   164  
   165  	for _, x := range s.AnyOf {
   166  		clause, err := newNestedValueValidation(&x)
   167  		if err != nil {
   168  			return nil, err
   169  		}
   170  		v.AnyOf = append(v.AnyOf, *clause)
   171  	}
   172  
   173  	for _, x := range s.OneOf {
   174  		clause, err := newNestedValueValidation(&x)
   175  		if err != nil {
   176  			return nil, err
   177  		}
   178  		v.OneOf = append(v.OneOf, *clause)
   179  	}
   180  
   181  	return v, nil
   182  }
   183  
   184  func newNestedValueValidation(s *apiextensions.JSONSchemaProps) (*NestedValueValidation, error) {
   185  	if s == nil {
   186  		return nil, nil
   187  	}
   188  
   189  	if err := validateUnsupportedFields(s); err != nil {
   190  		return nil, err
   191  	}
   192  
   193  	vv, err := newValueValidation(s)
   194  	if err != nil {
   195  		return nil, err
   196  	}
   197  
   198  	g, err := newGenerics(s)
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  
   203  	x, err := newExtensions(s)
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  
   208  	v := &NestedValueValidation{
   209  		ValueValidation:     *vv,
   210  		ForbiddenGenerics:   *g,
   211  		ForbiddenExtensions: *x,
   212  	}
   213  
   214  	if s.Items != nil {
   215  		if len(s.Items.JSONSchemas) > 0 {
   216  			// we validate that it is not an array
   217  			return nil, fmt.Errorf("OpenAPIV3Schema 'items' must be a schema, but is an array")
   218  		}
   219  		nvv, err := newNestedValueValidation(s.Items.Schema)
   220  		if err != nil {
   221  			return nil, err
   222  		}
   223  		v.Items = nvv
   224  	}
   225  	if s.Properties != nil {
   226  		v.Properties = make(map[string]NestedValueValidation, len(s.Properties))
   227  		for k, x := range s.Properties {
   228  			nvv, err := newNestedValueValidation(&x)
   229  			if err != nil {
   230  				return nil, err
   231  			}
   232  			v.Properties[k] = *nvv
   233  		}
   234  	}
   235  
   236  	return v, nil
   237  }
   238  
   239  func newExtensions(s *apiextensions.JSONSchemaProps) (*Extensions, error) {
   240  	if s == nil {
   241  		return nil, nil
   242  	}
   243  
   244  	ret := &Extensions{
   245  		XEmbeddedResource: s.XEmbeddedResource,
   246  		XIntOrString:      s.XIntOrString,
   247  		XListMapKeys:      s.XListMapKeys,
   248  		XListType:         s.XListType,
   249  		XMapType:          s.XMapType,
   250  	}
   251  	if err := apiextensionsv1.Convert_apiextensions_ValidationRules_To_v1_ValidationRules(&s.XValidations, &ret.XValidations, nil); err != nil {
   252  		return nil, err
   253  	}
   254  
   255  	if s.XPreserveUnknownFields != nil {
   256  		if !*s.XPreserveUnknownFields {
   257  			return nil, fmt.Errorf("internal error: 'x-kubernetes-preserve-unknown-fields' must be true or undefined")
   258  		}
   259  		ret.XPreserveUnknownFields = true
   260  	}
   261  
   262  	return ret, nil
   263  }
   264  
   265  // validateUnsupportedFields checks that those fields rejected by validation are actually unset.
   266  func validateUnsupportedFields(s *apiextensions.JSONSchemaProps) error {
   267  	if len(s.ID) > 0 {
   268  		return fmt.Errorf("OpenAPIV3Schema 'id' is not supported")
   269  	}
   270  	if len(s.Schema) > 0 {
   271  		return fmt.Errorf("OpenAPIV3Schema 'schema' is not supported")
   272  	}
   273  	if s.Ref != nil && len(*s.Ref) > 0 {
   274  		return fmt.Errorf("OpenAPIV3Schema '$ref' is not supported")
   275  	}
   276  	if len(s.PatternProperties) > 0 {
   277  		return fmt.Errorf("OpenAPIV3Schema 'patternProperties' is not supported")
   278  	}
   279  	if len(s.Dependencies) > 0 {
   280  		return fmt.Errorf("OpenAPIV3Schema 'dependencies' is not supported")
   281  	}
   282  	if s.AdditionalItems != nil {
   283  		return fmt.Errorf("OpenAPIV3Schema 'additionalItems' is not supported")
   284  	}
   285  	if len(s.Definitions) > 0 {
   286  		return fmt.Errorf("OpenAPIV3Schema 'definitions' is not supported")
   287  	}
   288  
   289  	return nil
   290  }
   291  

View as plain text