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 }