crd

package
v0.4.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 30, 2022 License: Apache-2.0 Imports: 22 Imported by: 0

Documentation

Overview

Package crd contains utilities for generating CustomResourceDefinitions and their corresponding OpenAPI validation schemata.

Markers

Markers live under the markers subpackage. Two types of markers exist: those that modify schema generation (for validation), and those that modify the rest of the CRD. See the subpackage for more information and all supported markers.

Collecting Types and Generating CRDs

The Parser is the entrypoint for collecting the information required to generate CRDs. Like loader and collector, its methods are idemptotent, not doing extra work if called multiple times.

Parser's method start with Need. Calling NeedXYZ indicates that XYZ should be made present in the eqivalent field in the Parser, where it can then be loaded from. Each Need method will in turn call Need on anything it needs.

In general, root packages should first be loaded into the Parser with NeedPackage. Then, CRDs can be generated with NeedCRDFor.

Errors are generally attached directly to the relevant Package with AddError.

Known Packages

There are a few types from Kubernetes that have special meaning, but don't have validation markers attached. Those specific types have overrides listed in KnownPackages that can be added as overrides to any parser.

Flattening

Once schemata are generated, they can be used directly by external tooling (like JSONSchema validators), but must first be "flattened" to not contain references before use in a CRD (Kubernetes doesn't allow references in the CRD's validation schema).

The Flattener built in to the Parser takes care of flattening out references when requesting the CRDs, but can be invoked manually. It will not modify the input schemata.

Flattened schemata may further be passed to FlattenEmbedded to remove the use of AllOf (which is used to describe embedded struct fields when references are in use). This done automatically when fetching CRDs.

Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Index

Constants

This section is empty.

Variables

View Source
var KnownPackages = map[string]PackageOverride{
	"k8s.io/api/core/v1": func(p *Parser, pkg *loader.Package) {

		p.Schemata[TypeIdent{Name: "Protocol", Package: pkg}] = apiext.JSONSchemaProps{
			Type:    "string",
			Default: &apiext.JSON{Raw: []byte(`"TCP"`)},
		}
		p.AddPackage(pkg)
	},

	"k8s.io/apimachinery/pkg/apis/meta/v1": func(p *Parser, pkg *loader.Package) {

		p.Schemata[TypeIdent{Name: "ObjectMeta", Package: pkg}] = apiext.JSONSchemaProps{
			Type: "object",
		}
		p.Schemata[TypeIdent{Name: "Time", Package: pkg}] = apiext.JSONSchemaProps{
			Type:   "string",
			Format: "date-time",
		}
		p.Schemata[TypeIdent{Name: "MicroTime", Package: pkg}] = apiext.JSONSchemaProps{
			Type:   "string",
			Format: "date-time",
		}
		p.Schemata[TypeIdent{Name: "Duration", Package: pkg}] = apiext.JSONSchemaProps{

			Type: "string",
		}
		p.Schemata[TypeIdent{Name: "Fields", Package: pkg}] = apiext.JSONSchemaProps{

			Type:                 "object",
			AdditionalProperties: &apiext.JSONSchemaPropsOrBool{Allows: true},
		}
		p.AddPackage(pkg)
	},

	"k8s.io/apimachinery/pkg/api/resource": func(p *Parser, pkg *loader.Package) {
		p.Schemata[TypeIdent{Name: "Quantity", Package: pkg}] = apiext.JSONSchemaProps{

			XIntOrString: true,
			AnyOf: []apiext.JSONSchemaProps{
				{Type: "integer"},
				{Type: "string"},
			},
			Pattern: "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
		}

	},

	"k8s.io/apimachinery/pkg/runtime": func(p *Parser, pkg *loader.Package) {
		p.Schemata[TypeIdent{Name: "RawExtension", Package: pkg}] = apiext.JSONSchemaProps{

			Type: "object",
		}
		p.AddPackage(pkg)
	},

	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured": func(p *Parser, pkg *loader.Package) {
		p.Schemata[TypeIdent{Name: "Unstructured", Package: pkg}] = apiext.JSONSchemaProps{
			Type: "object",
		}
		p.AddPackage(pkg)
	},

	"k8s.io/apimachinery/pkg/util/intstr": func(p *Parser, pkg *loader.Package) {
		p.Schemata[TypeIdent{Name: "IntOrString", Package: pkg}] = apiext.JSONSchemaProps{
			XIntOrString: true,
			AnyOf: []apiext.JSONSchemaProps{
				{Type: "integer"},
				{Type: "string"},
			},
		}

	},

	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1": func(p *Parser, pkg *loader.Package) {
		p.Schemata[TypeIdent{Name: "JSON", Package: pkg}] = apiext.JSONSchemaProps{
			XPreserveUnknownFields: boolPtr(true),
		}
		p.AddPackage(pkg)
	},
	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1": func(p *Parser, pkg *loader.Package) {
		p.Schemata[TypeIdent{Name: "JSON", Package: pkg}] = apiext.JSONSchemaProps{
			XPreserveUnknownFields: boolPtr(true),
		}
		p.AddPackage(pkg)
	},
}

KnownPackages overrides types in some comment packages that have custom validation but don't have validation markers on them (since they're from core Kubernetes).

Functions

func AddKnownTypes

func AddKnownTypes(parser *Parser)

AddKnownTypes registers the packages overrides in KnownPackages with the given parser.

func AsVersion

AsVersion converts a CRD from the canonical internal form (currently v1) to some external form.

func EditSchema

func EditSchema(schema *apiext.JSONSchemaProps, visitor SchemaVisitor)

EditSchema walks the given schema using the given visitor. Actual pointers to each schema node are passed to the visitor, so any changes made by the visitor will be reflected to the passed-in schema.

func FindKubeKinds

func FindKubeKinds(parser *Parser, metav1Pkg *loader.Package) map[schema.GroupKind]struct{}

FindKubeKinds locates all types that contain TypeMeta and ObjectMeta (and thus may be a Kubernetes object), and returns the corresponding group-kinds.

func FindMetav1

func FindMetav1(roots []*loader.Package) *loader.Package

FindMetav1 locates the actual package representing metav1 amongst the imports of the roots.

func FlattenEmbedded

func FlattenEmbedded(schema *apiext.JSONSchemaProps, errRec ErrorRecorder) *apiext.JSONSchemaProps

FlattenEmbedded flattens embedded fields (represented via AllOf) which have already had their references resolved into simple properties in the containing schema.

func MergeIdenticalVersionInfo

func MergeIdenticalVersionInfo(crd *apiextv1beta1.CustomResourceDefinition)

MergeIdenticalVersionInfo makes sure that components of the Versions field that are identical across all versions get merged into the top-level fields in v1beta1.

This is required by the Kubernetes API server validation.

The reason is that a v1beta1 -> v1 -> v1beta1 conversion cycle would need to round-trip identically, v1 doesn't have top-level subresources, and without this restriction it would be ambiguous how a v1-with-identical-subresources converts into a v1beta1).

func RefParts

func RefParts(ref string) (typ string, pkgName string, err error)

RefParts splits a reference produced by the schema generator into its component type name and package name (if it's a cross-package reference). Note that referenced packages *must* be looked up relative to the current package.

func TruncateDescription

func TruncateDescription(schema *apiext.JSONSchemaProps, maxLen int)

TruncateDescription truncates the description of fields in given schema if it exceeds maxLen. It tries to chop off the description at the closest sentence boundary.

func TypeRefLink(pkgName, typeName string) string

TypeRefLink creates a definition link for the given type and package.

Types

type ErrorRecorder

type ErrorRecorder interface {
	// AddError records that the given error occurred.
	// See the documentation on loader.Package.AddError for more information.
	AddError(error)
}

ErrorRecorder knows how to record errors. It wraps the part of pkg/loader.Package that we need to record errors in places were it might not make sense to have a loader.Package

type Flattener

type Flattener struct {
	// Parser is used to lookup package and type details, and parse in new packages.
	Parser *Parser

	LookupReference func(ref string, contextPkg *loader.Package) (TypeIdent, error)
	// contains filtered or unexported fields
}

Flattener knows how to take a root type, and flatten all references in it into a single, flat type. Flattened types are cached, so it's relatively cheap to make repeated calls with the same type.

func (*Flattener) FlattenSchema

func (f *Flattener) FlattenSchema(baseSchema apiext.JSONSchemaProps, currentPackage *loader.Package) *apiext.JSONSchemaProps

FlattenSchema flattens the given schema, removing any references. It deep-copies the schema first, so the input schema won't be affected.

func (*Flattener) FlattenType

func (f *Flattener) FlattenType(typ TypeIdent) *apiext.JSONSchemaProps

FlattenType flattens the given pre-loaded type, removing any references from it. It deep-copies the schema first, so it won't affect the parser's version of the schema.

type Generator

type Generator struct {
	// TrivialVersions indicates that we should produce a single-version CRD.
	//
	// Single "trivial-version" CRDs are compatible with older (pre 1.13)
	// Kubernetes API servers.  The storage version's schema will be used as
	// the CRD's schema.
	//
	// Only works with the v1beta1 CRD version.
	TrivialVersions bool `marker:",optional"`

	// PreserveUnknownFields indicates whether or not we should turn off pruning.
	//
	// Left unspecified, it'll default to true when only a v1beta1 CRD is
	// generated (to preserve compatibility with older versions of this tool),
	// or false otherwise.
	//
	// It's required to be false for v1 CRDs.
	PreserveUnknownFields *bool `marker:",optional"`

	// AllowDangerousTypes allows types which are usually omitted from CRD generation
	// because they are not recommended.
	//
	// Currently the following additional types are allowed when this is true:
	// float32
	// float64
	//
	// Left unspecified, the default is false
	AllowDangerousTypes *bool `marker:",optional"`

	// MaxDescLen specifies the maximum description length for fields in CRD's OpenAPI schema.
	//
	// 0 indicates drop the description for all fields completely.
	// n indicates limit the description to at most n characters and truncate the description to
	// closest sentence boundary if it exceeds n characters.
	MaxDescLen *int `marker:",optional"`

	// CRDVersions specifies the target API versions of the CRD type itself to
	// generate. Defaults to v1.
	//
	// The first version listed will be assumed to be the "default" version and
	// will not get a version suffix in the output filename.
	//
	// You'll need to use "v1" to get support for features like defaulting,
	// along with an API server that supports it (Kubernetes 1.16+).
	CRDVersions []string `marker:"crdVersions,optional"`
}

Generator generates CustomResourceDefinition objects.

func (Generator) CheckFilter

func (Generator) CheckFilter() loader.NodeFilter

func (Generator) Generate

func (g Generator) Generate(ctx *genall.GenerationContext) error

func (Generator) Help

func (Generator) RegisterMarkers

func (Generator) RegisterMarkers(into *markers.Registry) error

type PackageOverride

type PackageOverride func(p *Parser, pkg *loader.Package)

PackageOverride overrides the loading of some package (potentially setting custom schemata, etc). It must call AddPackage if it wants to continue with the default loading behavior.

type Parser

type Parser struct {
	Collector *markers.Collector

	// Types contains the known TypeInfo for this parser.
	Types map[TypeIdent]*markers.TypeInfo
	// Schemata contains the known OpenAPI JSONSchemata for this parser.
	Schemata map[TypeIdent]apiext.JSONSchemaProps
	// GroupVersions contains the known group-versions of each package in this parser.
	GroupVersions map[*loader.Package]schema.GroupVersion
	// CustomResourceDefinitions contains the known CustomResourceDefinitions for types in this parser.
	CustomResourceDefinitions map[schema.GroupKind]apiext.CustomResourceDefinition
	// FlattenedSchemata contains fully flattened schemata for use in building
	// CustomResourceDefinition validation.  Each schema has been flattened by the flattener,
	// and then embedded fields have been flattened with FlattenEmbedded.
	FlattenedSchemata map[TypeIdent]apiext.JSONSchemaProps

	// PackageOverrides indicates that the loading of any package with
	// the given path should be handled by the given overrider.
	PackageOverrides map[string]PackageOverride

	// checker stores persistent partial type-checking/reference-traversal information.
	Checker *loader.TypeChecker

	// AllowDangerousTypes controls the handling of non-recommended types such as float. If
	// false (the default), these types are not supported.
	// There is a continuum here:
	//    1. Types that are always supported.
	//    2. Types that are allowed by default, but not recommended (warning emitted when they are encountered as per PR #443).
	//       Possibly they are allowed by default for historical reasons and may even be "on their way out" at some point in the future.
	//    3. Types that are not allowed by default, not recommended, but there are some legitimate reasons to need them in certain corner cases.
	//       Possibly these types should also emit a warning as per PR #443 even when they are "switched on" (an integration point between
	//       this feature and #443 if desired). This is the category that this flag deals with.
	//    4. Types that are not allowed and will not be allowed, possibly because it just "doesn't make sense" or possibly
	//       because the implementation is too difficult/clunky to promote them to category 3.
	// TODO: Should we have a more formal mechanism for putting "type patterns" in each of the above categories?
	AllowDangerousTypes bool
	// contains filtered or unexported fields
}

Parser knows how to parse out CRD information and generate OpenAPI schemata from some collection of types and markers. Most methods on Parser cache their results automatically, and thus may be called any number of times.

func (*Parser) AddPackage

func (p *Parser) AddPackage(pkg *loader.Package)

AddPackage indicates that types and type-checking information is needed for the the given package, *ignoring* overrides. Generally, consumers should call NeedPackage, while PackageOverrides should call AddPackage to continue with the normal loading procedure.

func (*Parser) LookupType

func (p *Parser) LookupType(pkg *loader.Package, name string) *markers.TypeInfo

LookupType fetches type info from Types.

func (*Parser) NeedCRDFor

func (p *Parser) NeedCRDFor(groupKind schema.GroupKind, maxDescLen *int)

NeedCRDFor requests the full CRD for the given group-kind. It requires that the packages containing the Go structs for that CRD have already been loaded with NeedPackage.

func (*Parser) NeedFlattenedSchemaFor

func (p *Parser) NeedFlattenedSchemaFor(typ TypeIdent)

func (*Parser) NeedPackage

func (p *Parser) NeedPackage(pkg *loader.Package)

NeedPackage indicates that types and type-checking information is needed for the given package.

func (*Parser) NeedSchemaFor

func (p *Parser) NeedSchemaFor(typ TypeIdent)

NeedSchemaFor indicates that a schema should be generated for the given type.

type SchemaMarker

type SchemaMarker interface {
	// ApplyToSchema is called after the rest of the schema for a given type
	// or field is generated, to modify the schema appropriately.
	ApplyToSchema(*apiext.JSONSchemaProps) error
}

SchemaMarker is any marker that needs to modify the schema of the underlying type or field.

type SchemaVisitor

type SchemaVisitor interface {
	// Visit is called for each schema node.  If it returns a visitor,
	// the visitor will be called on each direct child node, and then
	// this visitor will be called again with `nil` to indicate that
	// all children have been visited.  If a nil visitor is returned,
	// children are not visited.
	//
	// It is *NOT* safe to save references to the given schema.
	// Make deepcopies if you need to keep things around beyond
	// the lifetime of the call.
	Visit(schema *apiext.JSONSchemaProps) SchemaVisitor
}

SchemaVisitor walks the nodes of a schema.

type SpecMarker

type SpecMarker interface {
	// ApplyToCRD applies this marker to the given CRD, in the given version
	// within that CRD.  It's called after everything else in the CRD is populated.
	ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error
}

SpecMarker is a marker that knows how to apply itself to a particular version in a CRD.

type TypeIdent

type TypeIdent struct {
	Package *loader.Package
	Name    string
}

TypeIdent represents some type in a Package.

func (TypeIdent) String

func (t TypeIdent) String() string

Directories

Path Synopsis
Package markers defines markers for generating schema valiation and CRD structure.
Package markers defines markers for generating schema valiation and CRD structure.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL