k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/schemaconv/proto_models.go (about)

     1  /*
     2  Copyright 2022 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 schemaconv
    18  
    19  import (
    20  	"errors"
    21  	"path"
    22  	"strings"
    23  
    24  	"k8s.io/kube-openapi/pkg/util/proto"
    25  	"sigs.k8s.io/structured-merge-diff/v4/schema"
    26  )
    27  
    28  // ToSchema converts openapi definitions into a schema suitable for structured
    29  // merge (i.e. kubectl apply v2).
    30  func ToSchema(models proto.Models) (*schema.Schema, error) {
    31  	return ToSchemaWithPreserveUnknownFields(models, false)
    32  }
    33  
    34  // ToSchemaWithPreserveUnknownFields converts openapi definitions into a schema suitable for structured
    35  // merge (i.e. kubectl apply v2), it will preserve unknown fields if specified.
    36  func ToSchemaWithPreserveUnknownFields(models proto.Models, preserveUnknownFields bool) (*schema.Schema, error) {
    37  	c := convert{
    38  		preserveUnknownFields: preserveUnknownFields,
    39  		output:                &schema.Schema{},
    40  	}
    41  	for _, name := range models.ListModels() {
    42  		model := models.LookupModel(name)
    43  
    44  		var a schema.Atom
    45  		c2 := c.push(name, &a)
    46  		model.Accept(c2)
    47  		c.pop(c2)
    48  
    49  		c.insertTypeDef(name, a)
    50  	}
    51  
    52  	if len(c.errorMessages) > 0 {
    53  		return nil, errors.New(strings.Join(c.errorMessages, "\n"))
    54  	}
    55  
    56  	c.addCommonTypes()
    57  	return c.output, nil
    58  }
    59  
    60  func (c *convert) makeRef(model proto.Schema, preserveUnknownFields bool) schema.TypeRef {
    61  	var tr schema.TypeRef
    62  	if r, ok := model.(*proto.Ref); ok {
    63  		if r.Reference() == "io.k8s.apimachinery.pkg.runtime.RawExtension" {
    64  			return schema.TypeRef{
    65  				NamedType: &untypedName,
    66  			}
    67  		}
    68  		// reference a named type
    69  		_, n := path.Split(r.Reference())
    70  		tr.NamedType = &n
    71  
    72  		mapRelationship, err := getMapElementRelationship(model.GetExtensions())
    73  
    74  		if err != nil {
    75  			c.reportError(err.Error())
    76  		}
    77  
    78  		// empty string means unset.
    79  		if len(mapRelationship) > 0 {
    80  			tr.ElementRelationship = &mapRelationship
    81  		}
    82  	} else {
    83  		// compute the type inline
    84  		c2 := c.push("inlined in "+c.currentName, &tr.Inlined)
    85  		c2.preserveUnknownFields = preserveUnknownFields
    86  		model.Accept(c2)
    87  		c.pop(c2)
    88  
    89  		if tr == (schema.TypeRef{}) {
    90  			// emit warning?
    91  			tr.NamedType = &untypedName
    92  		}
    93  	}
    94  	return tr
    95  }
    96  
    97  func (c *convert) VisitKind(k *proto.Kind) {
    98  	preserveUnknownFields := c.preserveUnknownFields
    99  	if p, ok := k.GetExtensions()["x-kubernetes-preserve-unknown-fields"]; ok && p == true {
   100  		preserveUnknownFields = true
   101  	}
   102  
   103  	a := c.top()
   104  	a.Map = &schema.Map{}
   105  	for _, name := range k.FieldOrder {
   106  		member := k.Fields[name]
   107  		tr := c.makeRef(member, preserveUnknownFields)
   108  		a.Map.Fields = append(a.Map.Fields, schema.StructField{
   109  			Name:    name,
   110  			Type:    tr,
   111  			Default: member.GetDefault(),
   112  		})
   113  	}
   114  
   115  	unions, err := makeUnions(k.GetExtensions())
   116  	if err != nil {
   117  		c.reportError(err.Error())
   118  		return
   119  	}
   120  	// TODO: We should check that the fields and discriminator
   121  	// specified in the union are actual fields in the struct.
   122  	a.Map.Unions = unions
   123  
   124  	if preserveUnknownFields {
   125  		a.Map.ElementType = schema.TypeRef{
   126  			NamedType: &deducedName,
   127  		}
   128  	}
   129  
   130  	a.Map.ElementRelationship, err = getMapElementRelationship(k.GetExtensions())
   131  	if err != nil {
   132  		c.reportError(err.Error())
   133  	}
   134  }
   135  
   136  func (c *convert) VisitArray(a *proto.Array) {
   137  	relationship, mapKeys, err := getListElementRelationship(a.GetExtensions())
   138  	if err != nil {
   139  		c.reportError(err.Error())
   140  	}
   141  
   142  	atom := c.top()
   143  	atom.List = &schema.List{
   144  		ElementType:         c.makeRef(a.SubType, c.preserveUnknownFields),
   145  		ElementRelationship: relationship,
   146  		Keys:                mapKeys,
   147  	}
   148  }
   149  
   150  func (c *convert) VisitMap(m *proto.Map) {
   151  	relationship, err := getMapElementRelationship(m.GetExtensions())
   152  	if err != nil {
   153  		c.reportError(err.Error())
   154  	}
   155  
   156  	a := c.top()
   157  	a.Map = &schema.Map{
   158  		ElementType:         c.makeRef(m.SubType, c.preserveUnknownFields),
   159  		ElementRelationship: relationship,
   160  	}
   161  }
   162  
   163  func (c *convert) VisitPrimitive(p *proto.Primitive) {
   164  	a := c.top()
   165  	if c.currentName == quantityResource {
   166  		a.Scalar = ptr(schema.Scalar("untyped"))
   167  	} else {
   168  		*a = convertPrimitive(p.Type, p.Format)
   169  	}
   170  }
   171  
   172  func (c *convert) VisitArbitrary(a *proto.Arbitrary) {
   173  	*c.top() = deducedDef.Atom
   174  }
   175  
   176  func (c *convert) VisitReference(proto.Reference) {
   177  	// Do nothing, we handle references specially
   178  }