@@ -73,6 +73,7 @@ type schemaContext struct {
7373
7474 allowDangerousTypes bool
7575 ignoreUnexportedFields bool
76+ opaque bool
7677}
7778
7879// newSchemaContext constructs a new schemaContext for the given package and schema requester.
@@ -99,6 +100,16 @@ func (c *schemaContext) ForInfo(info *markers.TypeInfo) *schemaContext {
99100 }
100101}
101102
103+ // ForInfoOpaque produces a new schemaContext with containing the same information
104+ // as this one, except with the given type information and marked as opaque.
105+ // An opaque context prevents the emission of $ref to the type's schema, suppressing
106+ // inherited type-level validations.
107+ func (c * schemaContext ) ForInfoOpaque (info * markers.TypeInfo ) * schemaContext {
108+ ctx := c .ForInfo (info )
109+ ctx .opaque = true
110+ return ctx
111+ }
112+
102113// requestSchema asks for the schema for a type in the package with the
103114// given import path.
104115func (c * schemaContext ) requestSchema (pkgPath , typeName string ) {
@@ -282,6 +293,12 @@ func localNamedToSchema(ctx *schemaContext, ident *ast.Ident) *apiextensionsv1.J
282293 // > Otherwise, the alias information is only in the type name, which
283294 // > points directly to the actual (aliased) type.
284295 if basicInfo .Name () != ident .Name {
296+ if ctx .opaque {
297+ return & apiextensionsv1.JSONSchemaProps {
298+ Type : typ ,
299+ Format : fmt ,
300+ }
301+ }
285302 ctx .requestSchema ("" , ident .Name )
286303 link := TypeRefLink ("" , ident .Name )
287304 return & apiextensionsv1.JSONSchemaProps {
@@ -303,8 +320,9 @@ func localNamedToSchema(ctx *schemaContext, ident *ast.Ident) *apiextensionsv1.J
303320 if pkg == ctx .pkg .Types {
304321 pkgPath = ""
305322 }
306- ctx .requestSchema (pkgPath , typeNameInfo .Name ())
307- link := TypeRefLink (pkgPath , typeNameInfo .Name ())
323+ if ! ctx .opaque {
324+ ctx .requestSchema (pkgPath , typeNameInfo .Name ())
325+ }
308326
309327 // In cases where we have a named type, apply the type and format from the named schema
310328 // to allow markers that need this information to apply correctly.
@@ -321,11 +339,17 @@ func localNamedToSchema(ctx *schemaContext, ident *ast.Ident) *apiextensionsv1.J
321339 }
322340 }
323341
324- return & apiextensionsv1.JSONSchemaProps {
342+ props := & apiextensionsv1.JSONSchemaProps {
325343 Type : typ ,
326344 Format : fmt ,
327- Ref : & link ,
328345 }
346+
347+ if ! ctx .opaque {
348+ link := TypeRefLink (pkgPath , typeNameInfo .Name ())
349+ props .Ref = & link
350+ }
351+
352+ return props
329353}
330354
331355// namedSchema creates a schema (ref) for an explicitly external type reference.
@@ -338,6 +362,11 @@ func namedToSchema(ctx *schemaContext, named *ast.SelectorExpr) *apiextensionsv1
338362 typeInfo := typeInfoRaw .(interface { Obj () * types.TypeName })
339363 typeNameInfo := typeInfo .Obj ()
340364 nonVendorPath := loader .NonVendorPath (typeNameInfo .Pkg ().Path ())
365+
366+ if ctx .opaque {
367+ return & apiextensionsv1.JSONSchemaProps {}
368+ }
369+
341370 ctx .requestSchema (nonVendorPath , typeNameInfo .Name ())
342371 link := TypeRefLink (nonVendorPath , typeNameInfo .Name ())
343372 return & apiextensionsv1.JSONSchemaProps {
@@ -522,6 +551,8 @@ func structToSchema(ctx *schemaContext, structType *ast.StructType) *apiextensio
522551 var propSchema * apiextensionsv1.JSONSchemaProps
523552 if field .Markers .Get (crdmarkers .SchemalessName ) != nil {
524553 propSchema = & apiextensionsv1.JSONSchemaProps {}
554+ } else if field .Markers .Get (crdmarkers .OpaqueFieldName ) != nil {
555+ propSchema = typeToSchema (ctx .ForInfoOpaque (& markers.TypeInfo {}), field .RawField .Type )
525556 } else {
526557 propSchema = typeToSchema (ctx .ForInfo (& markers.TypeInfo {}), field .RawField .Type )
527558 }
0 commit comments