go.*: Update k8s packages

- update k8s client_go
 - update k8s apiextensions-apiserver
 - update k8s controller-tools

Signed-off-by: leonnicolas <leonloechner@gmx.de>
This commit is contained in:
leonnicolas
2022-04-23 11:01:19 +02:00
parent e20d13ace0
commit 3eaacc01ae
762 changed files with 58552 additions and 14514 deletions

View File

@@ -6,7 +6,6 @@ import (
apiextinternal "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
@@ -22,13 +21,15 @@ func init() {
if err := apiext.AddToScheme(conversionScheme); err != nil {
panic("must be able to add apiextensions/v1 to the CRD conversion Scheme")
}
if err := apiextv1beta1.AddToScheme(conversionScheme); err != nil {
panic("must be able to add apiextensions/v1beta1 to the CRD conversion Scheme")
}
}
// AsVersion converts a CRD from the canonical internal form (currently v1) to some external form.
func AsVersion(original apiext.CustomResourceDefinition, gv schema.GroupVersion) (runtime.Object, error) {
// TODO: Do we need to keep maintaining this conversion function
// post 1.22 when only CRDv1 is served by the apiserver?
if gv == apiextv1beta1.SchemeGroupVersion {
return nil, fmt.Errorf("apiVersion %q is not supported", gv.String())
}
// We can use the internal versions an existing conversions from kubernetes, since they're not in k/k itself.
// This punts the problem of conversion down the road for a future maintainer (or future instance of @directxman12)
// when we have to support older versions that get removed, or when API machinery decides to yell at us for this
@@ -40,83 +41,3 @@ func AsVersion(original apiext.CustomResourceDefinition, gv schema.GroupVersion)
return conversionScheme.ConvertToVersion(intVer, gv)
}
// mergeIdenticalSubresources checks to see if subresources are identical across
// all versions, and if so, merges them into a top-level version.
//
// This assumes you're not using trivial versions.
func mergeIdenticalSubresources(crd *apiextv1beta1.CustomResourceDefinition) {
subres := crd.Spec.Versions[0].Subresources
for _, ver := range crd.Spec.Versions {
if ver.Subresources == nil || !equality.Semantic.DeepEqual(subres, ver.Subresources) {
// either all nil, or not identical
return
}
}
// things are identical if we've gotten this far, so move the subresources up
// and discard the identical per-version ones
crd.Spec.Subresources = subres
for i := range crd.Spec.Versions {
crd.Spec.Versions[i].Subresources = nil
}
}
// mergeIdenticalSchemata checks to see if schemata are identical across
// all versions, and if so, merges them into a top-level version.
//
// This assumes you're not using trivial versions.
func mergeIdenticalSchemata(crd *apiextv1beta1.CustomResourceDefinition) {
schema := crd.Spec.Versions[0].Schema
for _, ver := range crd.Spec.Versions {
if ver.Schema == nil || !equality.Semantic.DeepEqual(schema, ver.Schema) {
// either all nil, or not identical
return
}
}
// things are identical if we've gotten this far, so move the schemata up
// to a single schema and discard the identical per-version ones
crd.Spec.Validation = schema
for i := range crd.Spec.Versions {
crd.Spec.Versions[i].Schema = nil
}
}
// mergeIdenticalPrinterColumns checks to see if schemata are identical across
// all versions, and if so, merges them into a top-level version.
//
// This assumes you're not using trivial versions.
func mergeIdenticalPrinterColumns(crd *apiextv1beta1.CustomResourceDefinition) {
cols := crd.Spec.Versions[0].AdditionalPrinterColumns
for _, ver := range crd.Spec.Versions {
if len(ver.AdditionalPrinterColumns) == 0 || !equality.Semantic.DeepEqual(cols, ver.AdditionalPrinterColumns) {
// either all nil, or not identical
return
}
}
// things are identical if we've gotten this far, so move the printer columns up
// and discard the identical per-version ones
crd.Spec.AdditionalPrinterColumns = cols
for i := range crd.Spec.Versions {
crd.Spec.Versions[i].AdditionalPrinterColumns = nil
}
}
// 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 MergeIdenticalVersionInfo(crd *apiextv1beta1.CustomResourceDefinition) {
if len(crd.Spec.Versions) > 0 {
mergeIdenticalSubresources(crd)
mergeIdenticalSchemata(crd)
mergeIdenticalPrinterColumns(crd)
}
}

View File

@@ -20,10 +20,8 @@ import (
"fmt"
"go/ast"
"go/types"
"os"
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextlegacy "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apimachinery/pkg/runtime/schema"
crdmarkers "sigs.k8s.io/controller-tools/pkg/crd/markers"
@@ -33,31 +31,16 @@ import (
"sigs.k8s.io/controller-tools/pkg/version"
)
// The identifier for v1 CustomResourceDefinitions.
const v1 = "v1"
// The default CustomResourceDefinition version to generate.
const defaultVersion = "v1"
const defaultVersion = v1
// +controllertools:marker:generateHelp
// Generator generates CustomResourceDefinition objects.
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.
//
@@ -78,6 +61,8 @@ type Generator struct {
// CRDVersions specifies the target API versions of the CRD type itself to
// generate. Defaults to v1.
//
// Currently, the only supported value is v1.
//
// The first version listed will be assumed to be the "default" version and
// will not get a version suffix in the output filename.
//
@@ -146,38 +131,8 @@ func (g Generator) Generate(ctx *genall.GenerationContext) error {
versionedCRDs[i] = conv
}
if g.TrivialVersions {
for i, crd := range versionedCRDs {
if crdVersions[i] == "v1beta1" {
toTrivialVersions(crd.(*apiextlegacy.CustomResourceDefinition))
}
}
}
// *If* we're only generating v1beta1 CRDs, default to `preserveUnknownFields: (unset)`
// for compatibility purposes. In any other case, default to false, since that's
// the sensible default and is required for v1.
v1beta1Only := len(crdVersions) == 1 && crdVersions[0] == "v1beta1"
switch {
case (g.PreserveUnknownFields == nil || *g.PreserveUnknownFields) && v1beta1Only:
crd := versionedCRDs[0].(*apiextlegacy.CustomResourceDefinition)
crd.Spec.PreserveUnknownFields = nil
case g.PreserveUnknownFields == nil, g.PreserveUnknownFields != nil && !*g.PreserveUnknownFields:
// it'll be false here (coming from v1) -- leave it as such
default:
return fmt.Errorf("you may only set PreserveUnknownFields to true with v1beta1 CRDs")
}
for i, crd := range versionedCRDs {
// defaults are not allowed to be specified in v1beta1 CRDs and
// decriptions are not allowed on the metadata regardless of version
// strip them before writing to a file
if crdVersions[i] == "v1beta1" {
removeDefaultsFromSchemas(crd.(*apiextlegacy.CustomResourceDefinition))
removeDescriptionFromMetadataLegacy(crd.(*apiextlegacy.CustomResourceDefinition))
} else {
removeDescriptionFromMetadata(crd.(*apiext.CustomResourceDefinition))
}
removeDescriptionFromMetadata(crd.(*apiext.CustomResourceDefinition))
var fileName string
if i == 0 {
fileName = fmt.Sprintf("%s_%s.yaml", crdRaw.Spec.Group, crdRaw.Spec.Names.Plural)
@@ -212,71 +167,6 @@ func removeDescriptionFromMetadataProps(v *apiext.JSONSchemaProps) {
}
}
func removeDescriptionFromMetadataLegacy(crd *apiextlegacy.CustomResourceDefinition) {
if crd.Spec.Validation != nil {
removeDescriptionFromMetadataPropsLegacy(crd.Spec.Validation.OpenAPIV3Schema)
}
for _, versionSpec := range crd.Spec.Versions {
if versionSpec.Schema != nil {
removeDescriptionFromMetadataPropsLegacy(versionSpec.Schema.OpenAPIV3Schema)
}
}
}
func removeDescriptionFromMetadataPropsLegacy(v *apiextlegacy.JSONSchemaProps) {
if m, ok := v.Properties["metadata"]; ok {
meta := &m
if meta.Description != "" {
meta.Description = ""
v.Properties["metadata"] = m
}
}
}
// removeDefaultsFromSchemas will remove all instances of default values being
// specified across all defined API versions
func removeDefaultsFromSchemas(crd *apiextlegacy.CustomResourceDefinition) {
if crd.Spec.Validation != nil {
removeDefaultsFromSchemaProps(crd.Spec.Validation.OpenAPIV3Schema)
}
for _, versionSpec := range crd.Spec.Versions {
if versionSpec.Schema != nil {
removeDefaultsFromSchemaProps(versionSpec.Schema.OpenAPIV3Schema)
}
}
}
// removeDefaultsFromSchemaProps will recurse into JSONSchemaProps to remove
// all instances of default values being specified
func removeDefaultsFromSchemaProps(v *apiextlegacy.JSONSchemaProps) {
if v == nil {
return
}
if v.Default != nil {
fmt.Fprintln(os.Stderr, "Warning: default unsupported in CRD version v1beta1, v1 required. Removing defaults.")
}
// nil-out the default field
v.Default = nil
for name, prop := range v.Properties {
// iter var reference is fine -- we handle the persistence of the modfications on the line below
//nolint:gosec
removeDefaultsFromSchemaProps(&prop)
v.Properties[name] = prop
}
if v.Items != nil {
removeDefaultsFromSchemaProps(v.Items.Schema)
for i := range v.Items.JSONSchemas {
props := v.Items.JSONSchemas[i]
removeDefaultsFromSchemaProps(&props)
v.Items.JSONSchemas[i] = props
}
}
}
// FixTopLevelMetadata resets the schema for the top-level metadata field which is needed for CRD validation
func FixTopLevelMetadata(crd apiext.CustomResourceDefinition) {
for _, v := range crd.Spec.Versions {
@@ -289,32 +179,6 @@ func FixTopLevelMetadata(crd apiext.CustomResourceDefinition) {
}
}
// toTrivialVersions strips out all schemata except for the storage schema,
// and moves that up into the root object. This makes the CRD compatible
// with pre 1.13 clusters.
func toTrivialVersions(crd *apiextlegacy.CustomResourceDefinition) {
var canonicalSchema *apiextlegacy.CustomResourceValidation
var canonicalSubresources *apiextlegacy.CustomResourceSubresources
var canonicalColumns []apiextlegacy.CustomResourceColumnDefinition
for i, ver := range crd.Spec.Versions {
if ver.Storage == true {
canonicalSchema = ver.Schema
canonicalSubresources = ver.Subresources
canonicalColumns = ver.AdditionalPrinterColumns
}
crd.Spec.Versions[i].Schema = nil
crd.Spec.Versions[i].Subresources = nil
crd.Spec.Versions[i].AdditionalPrinterColumns = nil
}
if canonicalSchema == nil {
return
}
crd.Spec.Validation = canonicalSchema
crd.Spec.Subresources = canonicalSubresources
crd.Spec.AdditionalPrinterColumns = canonicalColumns
}
// addAttribution adds attribution info to indicate controller-gen tool was used
// to generate this CRD definition along with the version info.
func addAttribution(crd *apiext.CustomResourceDefinition) {

View File

@@ -101,7 +101,7 @@ type StructType string
func (l ListType) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
if schema.Type != "array" {
return fmt.Errorf("must apply listType to an array")
return fmt.Errorf("must apply listType to an array, found %s", schema.Type)
}
if l != "map" && l != "atomic" && l != "set" {
return fmt.Errorf(`ListType must be either "map", "set" or "atomic"`)
@@ -115,7 +115,7 @@ func (l ListType) ApplyFirst() {}
func (l ListMapKey) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
if schema.Type != "array" {
return fmt.Errorf("must apply listMapKey to an array")
return fmt.Errorf("must apply listMapKey to an array, found %s", schema.Type)
}
if schema.XListType == nil || *schema.XListType != "map" {
return fmt.Errorf("must apply listMapKey to an associative-list")

View File

@@ -66,6 +66,7 @@ var ValidationMarkers = mustMakeAllWithPrefix("kubebuilder:validation", markers.
Type(""),
XPreserveUnknownFields{},
XEmbeddedResource{},
XIntOrString{},
)
// FieldOnlyMarkers list field-specific validation markers (i.e. those markers that don't make
@@ -232,6 +233,14 @@ type XPreserveUnknownFields struct{}
// field, yet it is possible. This can be combined with PreserveUnknownFields.
type XEmbeddedResource struct{}
// +controllertools:marker:generateHelp:category="CRD validation"
// IntOrString marks a fields as an IntOrString.
//
// This is required when applying patterns or other validations to an IntOrString
// field. Knwon information about the type is applied during the collapse phase
// and as such is not normally available during marker application.
type XIntOrString struct{}
// +controllertools:marker:generateHelp:category="CRD validation"
// Schemaless marks a field as being a schemaless object.
//
@@ -298,8 +307,11 @@ func (m MinLength) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
return nil
}
func (m Pattern) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
if schema.Type != "string" {
return fmt.Errorf("must apply pattern to a string")
// Allow string types or IntOrStrings. An IntOrString will still
// apply the pattern validation when a string is detected, the pattern
// will not apply to ints though.
if schema.Type != "string" && !schema.XIntOrString {
return fmt.Errorf("must apply pattern to a `string` or `IntOrString`")
}
schema.Pattern = string(m)
return nil
@@ -406,3 +418,13 @@ func (m XEmbeddedResource) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
schema.XEmbeddedResource = true
return nil
}
// NB(JoelSpeed): we use this property in other markers here,
// which means the "XIntOrString" marker *must* be applied first.
func (m XIntOrString) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
schema.XIntOrString = true
return nil
}
func (m XIntOrString) ApplyFirst() {}

View File

@@ -1,3 +1,4 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
@@ -48,7 +49,7 @@ func (DeprecatedVersion) Help() *markers.DefinitionHelp {
Details: "",
},
FieldHelp: map[string]markers.DetailedHelp{
"Warning": markers.DetailedHelp{
"Warning": {
Summary: "message to be shown on the deprecated version",
Details: "",
},
@@ -116,7 +117,7 @@ func (ListType) Help() *markers.DefinitionHelp {
Category: "CRD processing",
DetailedHelp: markers.DetailedHelp{
Summary: "specifies the type of data-structure that the list represents (map, set, atomic). ",
Details: "Possible data-structure types of a list are: \n - \"map\": it needs to have a key field, which will be used to build an associative list. A typical example is a the pod container list, which is indexed by the container name. \n - \"set\": Fields need to be \"scalar\", and there can be only one occurrence of each. \n - \"atomic\": All the fields in the list are treated as a single value, are typically manipulated together by the same actor.",
Details: "Possible data-structure types of a list are: \n - \"map\": it needs to have a key field, which will be used to build an associative list. A typical example is a the pod container list, which is indexed by the container name. \n - \"set\": Fields need to be \"scalar\", and there can be only one occurrence of each. \n - \"atomic\": All the fields in the list are treated as a single value, are typically manipulated together by the same actor.",
},
FieldHelp: map[string]markers.DetailedHelp{},
}
@@ -127,7 +128,7 @@ func (MapType) Help() *markers.DefinitionHelp {
Category: "CRD processing",
DetailedHelp: markers.DetailedHelp{
Summary: "specifies the level of atomicity of the map; i.e. whether each item in the map is independent of the others, or all fields are treated as a single unit. ",
Details: "Possible values: \n - \"granular\": items in the map are independent of each other, and can be manipulated by different actors. This is the default behavior. \n - \"atomic\": all fields are treated as one unit. Any changes have to replace the entire map.",
Details: "Possible values: \n - \"granular\": items in the map are independent of each other, and can be manipulated by different actors. This is the default behavior. \n - \"atomic\": all fields are treated as one unit. Any changes have to replace the entire map.",
},
FieldHelp: map[string]markers.DetailedHelp{},
}
@@ -360,7 +361,7 @@ func (StructType) Help() *markers.DefinitionHelp {
Category: "CRD processing",
DetailedHelp: markers.DetailedHelp{
Summary: "specifies the level of atomicity of the struct; i.e. whether each field in the struct is independent of the others, or all fields are treated as a single unit. ",
Details: "Possible values: \n - \"granular\": fields in the struct are independent of each other, and can be manipulated by different actors. This is the default behavior. \n - \"atomic\": all fields are treated as one unit. Any changes have to replace the entire struct.",
Details: "Possible values: \n - \"granular\": fields in the struct are independent of each other, and can be manipulated by different actors. This is the default behavior. \n - \"atomic\": all fields are treated as one unit. Any changes have to replace the entire struct.",
},
FieldHelp: map[string]markers.DetailedHelp{},
}
@@ -445,6 +446,17 @@ func (XEmbeddedResource) Help() *markers.DefinitionHelp {
}
}
func (XIntOrString) Help() *markers.DefinitionHelp {
return &markers.DefinitionHelp{
Category: "CRD validation",
DetailedHelp: markers.DetailedHelp{
Summary: "IntOrString marks a fields as an IntOrString. ",
Details: "This is required when applying patterns or other validations to an IntOrString field. Knwon information about the type is applied during the collapse phase and as such is not normally available during marker application.",
},
FieldHelp: map[string]markers.DetailedHelp{},
}
}
func (XPreserveUnknownFields) Help() *markers.DefinitionHelp {
return &markers.DefinitionHelp{
Category: "CRD processing",

View File

@@ -17,8 +17,10 @@ limitations under the License.
package crd
import (
"errors"
"fmt"
"go/ast"
"go/token"
"go/types"
"strings"
@@ -37,12 +39,10 @@ const (
defPrefix = "#/definitions/"
)
var (
// byteType is the types.Type for byte (see the types documention
// for why we need to look this up in the Universe), saved
// for quick comparison.
byteType = types.Universe.Lookup("byte").Type()
)
// byteType is the types.Type for byte (see the types documention
// for why we need to look this up in the Universe), saved
// for quick comparison.
var byteType = types.Universe.Lookup("byte").Type()
// SchemaMarker is any marker that needs to modify the schema of the underlying type or field.
type SchemaMarker interface {
@@ -109,6 +109,15 @@ func (c *schemaContext) requestSchema(pkgPath, typeName string) {
// infoToSchema creates a schema for the type in the given set of type information.
func infoToSchema(ctx *schemaContext) *apiext.JSONSchemaProps {
// If the obj implements a JSON marshaler and has a marker, use the markers value and do not traverse as
// the marshaler could be doing anything. If there is no marker, fall back to traversing.
if obj := ctx.pkg.Types.Scope().Lookup(ctx.info.Name); obj != nil && implementsJSONMarshaler(obj.Type()) {
schema := &apiext.JSONSchemaProps{}
applyMarkers(ctx, ctx.info.Markers, schema, ctx.info.RawSpec.Type)
if schema.Type != "" {
return schema
}
}
return typeToSchema(ctx, ctx.info.RawSpec.Type)
}
@@ -298,14 +307,12 @@ func mapToSchema(ctx *schemaContext, mapType *ast.MapType) *apiext.JSONSchemaPro
valSchema = namedToSchema(ctx.ForInfo(&markers.TypeInfo{}), val)
case *ast.ArrayType:
valSchema = arrayToSchema(ctx.ForInfo(&markers.TypeInfo{}), val)
if valSchema.Type == "array" && valSchema.Items.Schema.Type != "string" {
ctx.pkg.AddError(loader.ErrFromNode(fmt.Errorf("map values must be a named type, not %T", mapType.Value), mapType.Value))
return &apiext.JSONSchemaProps{}
}
case *ast.StarExpr:
valSchema = typeToSchema(ctx.ForInfo(&markers.TypeInfo{}), val)
case *ast.MapType:
valSchema = typeToSchema(ctx.ForInfo(&markers.TypeInfo{}), val)
default:
ctx.pkg.AddError(loader.ErrFromNode(fmt.Errorf("map values must be a named type, not %T", mapType.Value), mapType.Value))
ctx.pkg.AddError(loader.ErrFromNode(fmt.Errorf("not a supported map value type: %T", mapType.Value), mapType.Value))
return &apiext.JSONSchemaProps{}
}
@@ -415,10 +422,13 @@ func builtinToType(basic *types.Basic, allowDangerousTypes bool) (typ string, fo
typ = "string"
case basicInfo&types.IsInteger != 0:
typ = "integer"
case basicInfo&types.IsFloat != 0 && allowDangerousTypes:
typ = "number"
case basicInfo&types.IsFloat != 0:
if allowDangerousTypes {
typ = "number"
} else {
return "", "", errors.New("found float, the usage of which is highly discouraged, as support for them varies across languages. Please consider serializing your float as string instead. If you are really sure you want to use them, re-run with crd:allowDangerousTypes=true")
}
default:
// NB(directxman12): floats are *NOT* allowed in kubernetes APIs
return "", "", fmt.Errorf("unsupported type %q", basic.String())
}
@@ -431,3 +441,16 @@ func builtinToType(basic *types.Basic, allowDangerousTypes bool) (typ string, fo
return typ, format, nil
}
// Open coded go/types representation of encoding/json.Marshaller
var jsonMarshaler = types.NewInterfaceType([]*types.Func{
types.NewFunc(token.NoPos, nil, "MarshalJSON",
types.NewSignature(nil, nil,
types.NewTuple(
types.NewVar(token.NoPos, nil, "", types.NewSlice(types.Universe.Lookup("byte").Type())),
types.NewVar(token.NoPos, nil, "", types.Universe.Lookup("error").Type())), false)),
}, nil).Complete()
func implementsJSONMarshaler(typ types.Type) bool {
return types.Implements(typ, jsonMarshaler) || types.Implements(types.NewPointer(typ), jsonMarshaler)
}

View File

@@ -1,3 +1,4 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
@@ -32,14 +33,6 @@ func (Generator) Help() *markers.DefinitionHelp {
Details: "",
},
FieldHelp: map[string]markers.DetailedHelp{
"TrivialVersions": {
Summary: "indicates that we should produce a single-version CRD. ",
Details: "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. \n Only works with the v1beta1 CRD version.",
},
"PreserveUnknownFields": {
Summary: "indicates whether or not we should turn off pruning. ",
Details: "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. \n It's required to be false for v1 CRDs.",
},
"AllowDangerousTypes": {
Summary: "allows types which are usually omitted from CRD generation because they are not recommended. ",
Details: "Currently the following additional types are allowed when this is true: float32 float64 \n Left unspecified, the default is false",
@@ -50,7 +43,7 @@ func (Generator) Help() *markers.DefinitionHelp {
},
"CRDVersions": {
Summary: "specifies the target API versions of the CRD type itself to generate. Defaults to v1. ",
Details: "The first version listed will be assumed to be the \"default\" version and will not get a version suffix in the output filename. \n You'll need to use \"v1\" to get support for features like defaulting, along with an API server that supports it (Kubernetes 1.16+).",
Details: "Currently, the only supported value is v1. \n The first version listed will be assumed to be the \"default\" version and will not get a version suffix in the output filename. \n You'll need to use \"v1\" to get support for features like defaulting, along with an API server that supports it (Kubernetes 1.16+).",
},
"GenerateEmbeddedObjectMeta": {
Summary: "specifies if any embedded ObjectMeta in the CRD should be generated",

View File

@@ -1,3 +1,4 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*

View File

@@ -100,6 +100,8 @@ type Runtime struct {
GenerationContext
// OutputRules defines how to output artifacts for each Generator.
OutputRules OutputRules
// ErrorWriter defines where to write error messages.
ErrorWriter io.Writer
}
// GenerationContext defines the common information needed for each Generator
@@ -133,7 +135,7 @@ func (g GenerationContext) WriteYAML(itemPath string, objs ...interface{}) error
if err != nil {
return err
}
n, err := out.Write(append([]byte("\n---\n"), yamlContent...))
n, err := out.Write(append([]byte("---\n"), yamlContent...))
if err != nil {
return err
}
@@ -188,8 +190,12 @@ func (g Generators) ForRoots(rootPaths ...string) (*Runtime, error) {
func (r *Runtime) Run() bool {
// TODO(directxman12): we could make this parallel,
// but we'd need to ensure all underlying machinery is threadsafe
if r.ErrorWriter == nil {
r.ErrorWriter = os.Stderr
}
if len(r.Generators) == 0 {
fmt.Fprintln(os.Stderr, "no generators to run")
fmt.Fprintln(r.ErrorWriter, "no generators to run")
return true
}
@@ -205,7 +211,7 @@ func (r *Runtime) Run() bool {
}
if err := (*gen).Generate(&ctx); err != nil {
fmt.Fprintln(os.Stderr, err)
fmt.Fprintln(r.ErrorWriter, err)
hadErrs = true
}
}

View File

@@ -1,3 +1,4 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*

View File

@@ -249,7 +249,7 @@ func (l *loader) typeCheck(pkg *Package) {
// it's possible to have a call to check in parallel to a call to this
// if one package in the package graph gets its dependency filtered out,
// but another doesn't (so one wants a "dummy" package here, and another
// but another doesn't (so one wants a "placeholder" package here, and another
// wants the full check).
//
// Thus, we need to lock here (at least for the time being) to avoid

View File

@@ -67,12 +67,22 @@ func extractDoc(node ast.Node, decl *ast.GenDecl) string {
// chop off the extraneous last part
outLines = outLines[:len(outLines)-1]
}
// respect double-newline meaning actual newline
for i, line := range outLines {
// Trim any extranous whitespace,
// for handling /*…*/-style comments,
// which have whitespace preserved in go/ast:
line = strings.TrimSpace(line)
// Respect that double-newline means
// actual newline:
if line == "" {
outLines[i] = "\n"
} else {
outLines[i] = line
}
}
return strings.Join(outLines, " ")
}

View File

@@ -1,3 +1,4 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*

View File

@@ -23,7 +23,6 @@ import (
"gopkg.in/yaml.v3"
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextlegacy "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -54,7 +53,6 @@ import (
// patches.
var (
legacyAPIExtVersion = apiextlegacy.SchemeGroupVersion.String()
currentAPIExtVersion = apiext.SchemeGroupVersion.String()
)
@@ -62,18 +60,8 @@ var (
// Generator patches existing CRDs with new schemata.
//
// For legacy (v1beta1) single-version CRDs, it will simply replace the global schema.
//
// For legacy (v1beta1) multi-version CRDs, and any v1 CRDs, it will replace
// schemata of existing versions and *clear the schema* from any versions not
// specified in the Go code. It will *not* add new versions, or remove old
// ones.
//
// For legacy multi-version CRDs with identical schemata, it will take care of
// lifting the per-version schema up to the global schema.
//
// It will generate output for each "CRD Version" (API version of the CRD type
// itself) , e.g. apiextensions/v1beta1 and apiextensions/v1) available.
// itself) , e.g. apiextensions/v1) available.
type Generator struct {
// ManifestsPath contains the CustomResourceDefinition YAML files.
ManifestsPath string `marker:"manifests"`
@@ -259,70 +247,16 @@ type partialCRD struct {
CRDVersion string
}
// setGlobalSchema sets the global schema for the v1beta1 apiext version in
// this set (if present, as per partialCRD.setGlobalSchema), and sets the
// versioned schemas (as per setVersionedSchemata) for the v1 version.
// setGlobalSchema sets the versioned schemas (as per setVersionedSchemata).
func (e *partialCRDSet) setGlobalSchema() error {
// there's no easy way to get a "random" key from a go map :-/
var schema apiext.JSONSchemaProps
for ver := range e.NewSchemata {
schema = e.NewSchemata[ver]
break
}
for _, crdInfo := range e.CRDVersions {
switch crdInfo.CRDVersion {
case legacyAPIExtVersion:
if err := crdInfo.setGlobalSchema(schema); err != nil {
return err
}
case currentAPIExtVersion:
// just set the schemata as normal for non-legacy versions
if err := crdInfo.setVersionedSchemata(e.NewSchemata); err != nil {
return err
}
if err := crdInfo.setVersionedSchemata(e.NewSchemata); err != nil {
return err
}
}
return nil
}
// setGlobalSchema sets the global schema to one of the schemata
// for this CRD. All schemata must be identical for this to be a valid operation.
func (e *partialCRD) setGlobalSchema(newSchema apiext.JSONSchemaProps) error {
if e.CRDVersion != legacyAPIExtVersion {
// no global schema, nothing to do
return fmt.Errorf("cannot set global schema on non-legacy CRD versions")
}
schema, err := legacySchema(newSchema)
if err != nil {
return fmt.Errorf("failed to convert schema to legacy form: %w", err)
}
schemaNodeTree, err := yamlop.ToYAML(schema)
if err != nil {
return err
}
schemaNodeTree = schemaNodeTree.Content[0] // get rid of the document node
yamlop.SetStyle(schemaNodeTree, 0) // clear the style so it defaults to auto-style-choice
if err := yamlop.SetNode(e.Yaml, *schemaNodeTree, "spec", "validation", "openAPIV3Schema"); err != nil {
return err
}
versions, found, err := e.getVersionsNode()
if err != nil {
return err
}
if !found {
return nil
}
for i, verNode := range versions.Content {
if err := yamlop.DeleteNode(verNode, "schema"); err != nil {
return fmt.Errorf("spec.versions[%d]: %w", i, err)
}
}
return nil
}
// getVersionsNode gets the YAML node of .spec.versions YAML mapping,
// if returning the node, and whether or not it was present.
func (e *partialCRD) getVersionsNode() (*yaml.Node, bool, error) {
@@ -382,16 +316,7 @@ func (e *partialCRD) setVersionedSchemata(newSchemata map[string]apiext.JSONSche
return fmt.Errorf("spec.versions[%d]: %w", i, err)
}
} else {
// TODO(directxman12): if this gets to be more than 2 versions, use polymorphism to clean this up
var verSchema interface{} = newSchema
if e.CRDVersion == legacyAPIExtVersion {
verSchema, err = legacySchema(newSchema)
if err != nil {
return fmt.Errorf("failed to convert schema to legacy form: %w", err)
}
}
schemaNodeTree, err := yamlop.ToYAML(verSchema)
schemaNodeTree, err := yamlop.ToYAML(newSchema)
if err != nil {
return fmt.Errorf("failed to convert schema to YAML: %w", err)
}
@@ -433,7 +358,10 @@ func crdsFromDirectory(ctx *genall.GenerationContext, dir string) (map[schema.Gr
if err := kyaml.Unmarshal(rawContent, &typeMeta); err != nil {
continue
}
if !isSupportedAPIExtGroupVer(typeMeta.APIVersion) || typeMeta.Kind != "CustomResourceDefinition" {
if !isSupportedAPIExtGroupVer(typeMeta.APIVersion) {
return nil, fmt.Errorf("load %q: apiVersion %q not supported", filepath.Join(dir, fileInfo.Name()), typeMeta.APIVersion)
}
if typeMeta.Kind != "CustomResourceDefinition" {
continue
}
@@ -443,14 +371,9 @@ func crdsFromDirectory(ctx *genall.GenerationContext, dir string) (map[schema.Gr
continue
}
groupKind := schema.GroupKind{Group: actualCRD.Spec.Group, Kind: actualCRD.Spec.Names.Kind}
var versions map[string]struct{}
if len(actualCRD.Spec.Versions) == 0 {
versions = map[string]struct{}{actualCRD.Spec.Version: {}}
} else {
versions = make(map[string]struct{}, len(actualCRD.Spec.Versions))
for _, ver := range actualCRD.Spec.Versions {
versions[ver.Name] = struct{}{}
}
versions := make(map[string]struct{}, len(actualCRD.Spec.Versions))
for _, ver := range actualCRD.Spec.Versions {
versions[ver.Name] = struct{}{}
}
// then actually unmarshal in a manner that preserves ordering, etc
@@ -480,9 +403,9 @@ func crdsFromDirectory(ctx *genall.GenerationContext, dir string) (map[schema.Gr
}
// isSupportedAPIExtGroupVer checks if the given string-form group-version
// is one of the known apiextensions versions (v1, v1beta1).
// is one of the known apiextensions versions (v1).
func isSupportedAPIExtGroupVer(groupVer string) bool {
return groupVer == currentAPIExtVersion || groupVer == legacyAPIExtVersion
return groupVer == currentAPIExtVersion
}
// crdIsh is a merged blob of CRD fields that looks enough like all versions of
@@ -502,23 +425,5 @@ type crdIsh struct {
Versions []struct {
Name string `json:"name"`
} `json:"versions"`
Version string `json:"version"`
} `json:"spec"`
}
// legacySchema jumps through some hoops to convert a v1 schema to a v1beta1 schema.
func legacySchema(origSchema apiext.JSONSchemaProps) (apiextlegacy.JSONSchemaProps, error) {
shellCRD := apiext.CustomResourceDefinition{}
shellCRD.APIVersion = currentAPIExtVersion
shellCRD.Kind = "CustomResourceDefinition"
shellCRD.Spec.Versions = []apiext.CustomResourceDefinitionVersion{
{Schema: &apiext.CustomResourceValidation{OpenAPIV3Schema: origSchema.DeepCopy()}},
}
legacyCRD, err := crdgen.AsVersion(shellCRD, apiextlegacy.SchemeGroupVersion)
if err != nil {
return apiextlegacy.JSONSchemaProps{}, err
}
return *legacyCRD.(*apiextlegacy.CustomResourceDefinition).Spec.Validation.OpenAPIV3Schema, nil
}

View File

@@ -1,3 +1,4 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
@@ -29,7 +30,7 @@ func (Generator) Help() *markers.DefinitionHelp {
Category: "",
DetailedHelp: markers.DetailedHelp{
Summary: "patches existing CRDs with new schemata. ",
Details: "For legacy (v1beta1) single-version CRDs, it will simply replace the global schema. \n For legacy (v1beta1) multi-version CRDs, and any v1 CRDs, it will replace schemata of existing versions and *clear the schema* from any versions not specified in the Go code. It will *not* add new versions, or remove old ones. \n For legacy multi-version CRDs with identical schemata, it will take care of lifting the per-version schema up to the global schema. \n It will generate output for each \"CRD Version\" (API version of the CRD type itself) , e.g. apiextensions/v1beta1 and apiextensions/v1) available.",
Details: "It will generate output for each \"CRD Version\" (API version of the CRD type itself) , e.g. apiextensions/v1) available.",
},
FieldHelp: map[string]markers.DetailedHelp{
"ManifestsPath": {
@@ -40,6 +41,10 @@ func (Generator) Help() *markers.DefinitionHelp {
Summary: "specifies the maximum description length for fields in CRD's OpenAPI schema. ",
Details: "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.",
},
"GenerateEmbeddedObjectMeta": {
Summary: "specifies if any embedded ObjectMeta in the CRD should be generated",
Details: "",
},
},
}
}

View File

@@ -36,7 +36,8 @@ import (
// The default {Mutating,Validating}WebhookConfiguration version to generate.
const (
defaultWebhookVersion = "v1"
v1 = "v1"
defaultWebhookVersion = v1
)
var (
@@ -47,7 +48,7 @@ var (
// supportedWebhookVersions returns currently supported API version of {Mutating,Validating}WebhookConfiguration.
func supportedWebhookVersions() []string {
return []string{defaultWebhookVersion, "v1beta1"}
return []string{defaultWebhookVersion}
}
// +controllertools:marker:generateHelp:category=Webhook
@@ -104,14 +105,12 @@ type Config struct {
Path string
// WebhookVersions specifies the target API versions of the {Mutating,Validating}WebhookConfiguration objects
// itself to generate. Defaults to v1.
// itself to generate. The only supported value is v1. Defaults to v1.
WebhookVersions []string `marker:"webhookVersions,optional"`
// AdmissionReviewVersions is an ordered list of preferred `AdmissionReview`
// versions the Webhook expects.
// For generating v1 {Mutating,Validating}WebhookConfiguration, this is mandatory.
// For generating v1beta1 {Mutating,Validating}WebhookConfiguration, this is optional, and default to v1beta1.
AdmissionReviewVersions []string `marker:"admissionReviewVersions,optional"`
AdmissionReviewVersions []string `marker:"admissionReviewVersions"`
}
// verbToAPIVariant converts a marker's verb to the proper value for the API.
@@ -331,9 +330,8 @@ func (Generator) Generate(ctx *genall.GenerationContext) error {
versionedWebhooks := make(map[string][]interface{}, len(supportedWebhookVersions))
for _, version := range supportedWebhookVersions {
if cfgs, ok := mutatingCfgs[version]; ok {
// All webhook config versions in supportedWebhookVersions have the same general form, with a few
// stricter requirements for v1. Since no conversion scheme exists for webhook configs, the v1
// type can be used for all versioned types in this context.
// The only possible version in supportedWebhookVersions is v1,
// so use it for all versioned types in this context.
objRaw := &admissionregv1.MutatingWebhookConfiguration{}
objRaw.SetGroupVersionKind(schema.GroupVersionKind{
Group: admissionregv1.SchemeGroupVersion.Group,
@@ -342,28 +340,24 @@ func (Generator) Generate(ctx *genall.GenerationContext) error {
})
objRaw.SetName("mutating-webhook-configuration")
objRaw.Webhooks = cfgs
switch version {
case admissionregv1.SchemeGroupVersion.Version:
for i := range objRaw.Webhooks {
// SideEffects is required in admissionregistration/v1, if this is not set or set to `Some` or `Known`,
// return an error
if err := checkSideEffectsForV1(objRaw.Webhooks[i].SideEffects); err != nil {
return err
}
// AdmissionReviewVersions is required in admissionregistration/v1, if this is not set,
// return an error
if len(objRaw.Webhooks[i].AdmissionReviewVersions) == 0 {
return fmt.Errorf("AdmissionReviewVersions is mandatory for v1 {Mutating,Validating}WebhookConfiguration")
}
for i := range objRaw.Webhooks {
// SideEffects is required in admissionregistration/v1, if this is not set or set to `Some` or `Known`,
// return an error
if err := checkSideEffectsForV1(objRaw.Webhooks[i].SideEffects); err != nil {
return err
}
// AdmissionReviewVersions is required in admissionregistration/v1, if this is not set,
// return an error
if len(objRaw.Webhooks[i].AdmissionReviewVersions) == 0 {
return fmt.Errorf("AdmissionReviewVersions is mandatory for v1 {Mutating,Validating}WebhookConfiguration")
}
}
versionedWebhooks[version] = append(versionedWebhooks[version], objRaw)
}
if cfgs, ok := validatingCfgs[version]; ok {
// All webhook config versions in supportedWebhookVersions have the same general form, with a few
// stricter requirements for v1. Since no conversion scheme exists for webhook configs, the v1
// type can be used for all versioned types in this context.
// The only possible version in supportedWebhookVersions is v1,
// so use it for all versioned types in this context.
objRaw := &admissionregv1.ValidatingWebhookConfiguration{}
objRaw.SetGroupVersionKind(schema.GroupVersionKind{
Group: admissionregv1.SchemeGroupVersion.Group,
@@ -372,19 +366,16 @@ func (Generator) Generate(ctx *genall.GenerationContext) error {
})
objRaw.SetName("validating-webhook-configuration")
objRaw.Webhooks = cfgs
switch version {
case admissionregv1.SchemeGroupVersion.Version:
for i := range objRaw.Webhooks {
// SideEffects is required in admissionregistration/v1, if this is not set or set to `Some` or `Known`,
// return an error
if err := checkSideEffectsForV1(objRaw.Webhooks[i].SideEffects); err != nil {
return err
}
// AdmissionReviewVersions is required in admissionregistration/v1, if this is not set,
// return an error
if len(objRaw.Webhooks[i].AdmissionReviewVersions) == 0 {
return fmt.Errorf("AdmissionReviewVersions is mandatory for v1 {Mutating,Validating}WebhookConfiguration")
}
for i := range objRaw.Webhooks {
// SideEffects is required in admissionregistration/v1, if this is not set or set to `Some` or `Known`,
// return an error
if err := checkSideEffectsForV1(objRaw.Webhooks[i].SideEffects); err != nil {
return err
}
// AdmissionReviewVersions is required in admissionregistration/v1, if this is not set,
// return an error
if len(objRaw.Webhooks[i].AdmissionReviewVersions) == 0 {
return fmt.Errorf("AdmissionReviewVersions is mandatory for v1 {Mutating,Validating}WebhookConfiguration")
}
}
versionedWebhooks[version] = append(versionedWebhooks[version], objRaw)

View File

@@ -1,3 +1,4 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
@@ -73,11 +74,11 @@ func (Config) Help() *markers.DefinitionHelp {
Details: "",
},
"WebhookVersions": {
Summary: "specifies the target API versions of the {Mutating,Validating}WebhookConfiguration objects itself to generate. Defaults to v1.",
Summary: "specifies the target API versions of the {Mutating,Validating}WebhookConfiguration objects itself to generate. The only supported value is v1. Defaults to v1.",
Details: "",
},
"AdmissionReviewVersions": {
Summary: "is an ordered list of preferred `AdmissionReview` versions the Webhook expects. For generating v1 {Mutating,Validating}WebhookConfiguration, this is mandatory. For generating v1beta1 {Mutating,Validating}WebhookConfiguration, this is optional, and default to v1beta1.",
Summary: "is an ordered list of preferred `AdmissionReview` versions the Webhook expects.",
Details: "",
},
},