зеркало из https://github.com/Azure/ARO-RP.git
go get "sigs.k8s.io/controller-tools@v0.5.0"
go get "sigs.k8s.io/controller-tools@v0.5.0" go mod tidy go mod vendor
This commit is contained in:
Родитель
2951076c30
Коммит
bb4ad52e63
9
go.sum
9
go.sum
|
@ -777,9 +777,10 @@ github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzz
|
|||
github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ=
|
||||
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||
github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
|
||||
github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
|
||||
github.com/gobuffalo/flect v0.2.1 h1:GPoRjEN0QObosV4XwuoWvSd5uSiL0N3e91/xqyY4crQ=
|
||||
github.com/gobuffalo/flect v0.2.1/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc=
|
||||
github.com/gobuffalo/flect v0.2.2 h1:PAVD7sp0KOdfswjAw9BpLCU9hXo7wFSzgpQ+zNeks/A=
|
||||
github.com/gobuffalo/flect v0.2.2/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc=
|
||||
github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
|
||||
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
|
||||
github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc=
|
||||
|
@ -1589,7 +1590,6 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
|
|||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
|
@ -2534,7 +2534,6 @@ golang.org/x/tools v0.0.0-20190909030654-5b82db07426d/go.mod h1:b+2E5dAYhXwXZwtn
|
|||
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190918214516-5a1a30219888/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
|
@ -2949,8 +2948,8 @@ rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
|||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9/go.mod h1:dzAXnQbTRyDlZPJX2SUPEqvnB+j7AJjtlox7PEwigU0=
|
||||
sigs.k8s.io/controller-runtime v0.6.4 h1:4013CKsBs5bEqo+LevzDett+LLxag/FjQWG94nVZ/9g=
|
||||
sigs.k8s.io/controller-runtime v0.6.4/go.mod h1:WlZNXcM0++oyaQt4B7C2lEE5JYRs8vJUzRP4N4JpdAY=
|
||||
sigs.k8s.io/controller-tools v0.3.0 h1:y3YD99XOyWaXkiF1kd41uRvfp/64teWcrEZFuHxPhJ4=
|
||||
sigs.k8s.io/controller-tools v0.3.0/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI=
|
||||
sigs.k8s.io/controller-tools v0.5.0 h1:3u2RCwOlp0cjCALAigpOcbAf50pE+kHSdueUosrC/AE=
|
||||
sigs.k8s.io/controller-tools v0.5.0/go.mod h1:JTsstrMpxs+9BUj6eGuAaEb6SDSPTeVtUyp0jmnAM/I=
|
||||
sigs.k8s.io/kubebuilder v1.0.9-0.20200618125005-36aa113dbe99/go.mod h1:FGPx0hvP73+bapzWoy5ePuhAJYgJjrFbPxgvWyortM0=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
||||
|
|
|
@ -15,13 +15,10 @@ func Capitalize(s string) string {
|
|||
// bob dylan = Bob dylan
|
||||
// widget_id = Widget_id
|
||||
func (i Ident) Capitalize() Ident {
|
||||
var x string
|
||||
if len(i.Parts) == 0 {
|
||||
return New("")
|
||||
}
|
||||
x = string(unicode.ToTitle(rune(i.Original[0])))
|
||||
if len(i.Original) > 1 {
|
||||
x += i.Original[1:]
|
||||
}
|
||||
return New(x)
|
||||
runes := []rune(i.Original)
|
||||
runes[0] = unicode.ToTitle(runes[0])
|
||||
return New(string(runes))
|
||||
}
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
// Humanize returns first letter of sentence capitalized.
|
||||
// Common acronyms are capitalized as well.
|
||||
// Other capital letters in string are left as provided.
|
||||
// employee_salary = Employee salary
|
||||
// employee_id = employee ID
|
||||
// employee_mobile_number = Employee mobile number
|
||||
// first_Name = First Name
|
||||
// firstName = First Name
|
||||
// employee_salary = Employee salary
|
||||
// employee_id = employee ID
|
||||
// employee_mobile_number = Employee mobile number
|
||||
// first_Name = First Name
|
||||
// firstName = First Name
|
||||
func Humanize(s string) string {
|
||||
return New(s).Humanize().String()
|
||||
}
|
||||
|
|
|
@ -38,9 +38,9 @@ func toParts(s string) []string {
|
|||
return []string{strings.ToUpper(s)}
|
||||
}
|
||||
var prev rune
|
||||
var x string
|
||||
var x strings.Builder
|
||||
x.Grow(len(s))
|
||||
for _, c := range s {
|
||||
cs := string(c)
|
||||
// fmt.Println("### cs ->", cs)
|
||||
// fmt.Println("### unicode.IsControl(c) ->", unicode.IsControl(c))
|
||||
// fmt.Println("### unicode.IsDigit(c) ->", unicode.IsDigit(c))
|
||||
|
@ -58,35 +58,38 @@ func toParts(s string) []string {
|
|||
}
|
||||
|
||||
if isSpace(c) {
|
||||
parts = xappend(parts, x)
|
||||
x = cs
|
||||
parts = xappend(parts, x.String())
|
||||
x.Reset()
|
||||
x.WriteRune(c)
|
||||
prev = c
|
||||
continue
|
||||
}
|
||||
|
||||
if unicode.IsUpper(c) && !unicode.IsUpper(prev) {
|
||||
parts = xappend(parts, x)
|
||||
x = cs
|
||||
parts = xappend(parts, x.String())
|
||||
x.Reset()
|
||||
x.WriteRune(c)
|
||||
prev = c
|
||||
continue
|
||||
}
|
||||
if unicode.IsUpper(c) && baseAcronyms[strings.ToUpper(x)] {
|
||||
parts = xappend(parts, x)
|
||||
x = cs
|
||||
if unicode.IsUpper(c) && baseAcronyms[strings.ToUpper(x.String())] {
|
||||
parts = xappend(parts, x.String())
|
||||
x.Reset()
|
||||
x.WriteRune(c)
|
||||
prev = c
|
||||
continue
|
||||
}
|
||||
if unicode.IsLetter(c) || unicode.IsDigit(c) || unicode.IsPunct(c) || c == '`' {
|
||||
prev = c
|
||||
x += cs
|
||||
x.WriteRune(c)
|
||||
continue
|
||||
}
|
||||
|
||||
parts = xappend(parts, x)
|
||||
x = ""
|
||||
parts = xappend(parts, x.String())
|
||||
x.Reset()
|
||||
prev = c
|
||||
}
|
||||
parts = xappend(parts, x)
|
||||
parts = xappend(parts, x.String())
|
||||
|
||||
return parts
|
||||
}
|
||||
|
@ -94,6 +97,19 @@ func toParts(s string) []string {
|
|||
var _ encoding.TextUnmarshaler = &Ident{}
|
||||
var _ encoding.TextMarshaler = &Ident{}
|
||||
|
||||
// LastPart returns the last part/word of the original string
|
||||
func (i *Ident) LastPart() string {
|
||||
if len(i.Parts) == 0 {
|
||||
return ""
|
||||
}
|
||||
return i.Parts[len(i.Parts)-1]
|
||||
}
|
||||
|
||||
// ReplaceSuffix creates a new Ident with the original suffix replaced by new
|
||||
func (i Ident) ReplaceSuffix(orig, new string) Ident {
|
||||
return New(strings.TrimSuffix(i.Original, orig) + new)
|
||||
}
|
||||
|
||||
//UnmarshalText unmarshalls byte array into the Ident
|
||||
func (i *Ident) UnmarshalText(data []byte) error {
|
||||
(*i) = New(string(data))
|
||||
|
|
|
@ -49,7 +49,6 @@ var singleToPlural = map[string]string{
|
|||
"crisis": "crises",
|
||||
"curriculum": "curriculums",
|
||||
"datum": "data",
|
||||
"dear": "dear",
|
||||
"deer": "deer",
|
||||
"diagnosis": "diagnoses",
|
||||
"die": "dice",
|
||||
|
@ -106,6 +105,7 @@ var singleToPlural = map[string]string{
|
|||
"prognosis": "prognoses",
|
||||
"prometheus": "prometheuses",
|
||||
"quiz": "quizzes",
|
||||
"quota": "quotas",
|
||||
"radius": "radiuses",
|
||||
"referendum": "referendums",
|
||||
"ress": "resses",
|
||||
|
@ -122,6 +122,7 @@ var singleToPlural = map[string]string{
|
|||
"swine": "swine",
|
||||
"syllabus": "syllabi",
|
||||
"symposium": "symposiums",
|
||||
"synapse": "synapses",
|
||||
"synopsis": "synopses",
|
||||
"tableau": "tableaus",
|
||||
"testis": "testes",
|
||||
|
@ -130,6 +131,7 @@ var singleToPlural = map[string]string{
|
|||
"tooth": "teeth",
|
||||
"trout": "trout",
|
||||
"tuna": "tuna",
|
||||
"vedalia": "vedalias",
|
||||
"vertebra": "vertebrae",
|
||||
"vertix": "vertices",
|
||||
"vita": "vitae",
|
||||
|
@ -163,7 +165,6 @@ var singularToPluralSuffixList = []singularToPluralSuffix{
|
|||
{"randum", "randa"},
|
||||
{"actus", "acti"},
|
||||
{"adium", "adia"},
|
||||
{"alias", "aliases"},
|
||||
{"basis", "basis"},
|
||||
{"child", "children"},
|
||||
{"chive", "chives"},
|
||||
|
@ -171,6 +172,7 @@ var singularToPluralSuffixList = []singularToPluralSuffix{
|
|||
{"hello", "hellos"},
|
||||
{"jeans", "jeans"},
|
||||
{"louse", "lice"},
|
||||
{"media", "media"},
|
||||
{"mouse", "mice"},
|
||||
{"movie", "movies"},
|
||||
{"oasis", "oasis"},
|
||||
|
@ -258,7 +260,6 @@ var singularToPluralSuffixList = []singularToPluralSuffix{
|
|||
{"ly", "lies"},
|
||||
{"my", "mies"},
|
||||
{"ny", "nies"},
|
||||
{"ox", "oxen"},
|
||||
{"py", "pies"},
|
||||
{"qy", "qies"},
|
||||
{"rf", "rves"},
|
||||
|
|
|
@ -15,12 +15,22 @@ func Pluralize(s string) string {
|
|||
return New(s).Pluralize().String()
|
||||
}
|
||||
|
||||
// PluralizeWithSize will pluralize a string taking a number number into account.
|
||||
// PluralizeWithSize("user", 1) = user
|
||||
// PluralizeWithSize("user", 2) = users
|
||||
func PluralizeWithSize(s string, i int) string {
|
||||
if i == 1 || i == -1 {
|
||||
return New(s).Singularize().String()
|
||||
}
|
||||
return New(s).Pluralize().String()
|
||||
}
|
||||
|
||||
// Pluralize returns a plural version of the string
|
||||
// user = users
|
||||
// person = people
|
||||
// datum = data
|
||||
func (i Ident) Pluralize() Ident {
|
||||
s := i.Original
|
||||
s := i.LastPart()
|
||||
if len(s) == 0 {
|
||||
return New("")
|
||||
}
|
||||
|
@ -33,11 +43,11 @@ func (i Ident) Pluralize() Ident {
|
|||
return i
|
||||
}
|
||||
if p, ok := singleToPlural[ls]; ok {
|
||||
return New(p)
|
||||
return i.ReplaceSuffix(s, p)
|
||||
}
|
||||
for _, r := range pluralRules {
|
||||
if strings.HasSuffix(ls, r.suffix) {
|
||||
return New(r.fn(s))
|
||||
return i.ReplaceSuffix(s, r.fn(s))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,16 @@ func Singularize(s string) string {
|
|||
return New(s).Singularize().String()
|
||||
}
|
||||
|
||||
// SingularizeWithSize will singular a string taking a number number into account.
|
||||
// SingularizeWithSize("user", 1) = user
|
||||
// SingularizeWithSize("user", 2) = users
|
||||
func SingularizeWithSize(s string, i int) string {
|
||||
if i == 1 || i == -1 {
|
||||
return New(s).Singularize().String()
|
||||
}
|
||||
return New(s).Pluralize().String()
|
||||
}
|
||||
|
||||
// Singularize returns a singular version of the string
|
||||
// users = user
|
||||
// data = datum
|
||||
|
|
|
@ -18,16 +18,17 @@ func Underscore(s string) string {
|
|||
// Nice to see you! = nice_to_see_you
|
||||
// widgetID = widget_id
|
||||
func (i Ident) Underscore() Ident {
|
||||
var out []string
|
||||
out := make([]string, 0, len(i.Parts))
|
||||
for _, part := range i.Parts {
|
||||
var x string
|
||||
var x strings.Builder
|
||||
x.Grow(len(part))
|
||||
for _, c := range part {
|
||||
if unicode.IsLetter(c) || unicode.IsDigit(c) {
|
||||
x += string(c)
|
||||
x.WriteRune(c)
|
||||
}
|
||||
}
|
||||
if x != "" {
|
||||
out = append(out, x)
|
||||
if x.Len() > 0 {
|
||||
out = append(out, x.String())
|
||||
}
|
||||
}
|
||||
return New(strings.ToLower(strings.Join(out, "_")))
|
||||
|
|
|
@ -356,7 +356,7 @@ github.com/go-toolsmith/strparse
|
|||
github.com/go-toolsmith/typep
|
||||
# github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b
|
||||
github.com/go-xmlfmt/xmlfmt
|
||||
# github.com/gobuffalo/flect v0.2.1
|
||||
# github.com/gobuffalo/flect v0.2.2
|
||||
github.com/gobuffalo/flect
|
||||
# github.com/gobwas/glob v0.2.3
|
||||
github.com/gobwas/glob
|
||||
|
@ -1777,7 +1777,7 @@ sigs.k8s.io/controller-runtime/pkg/webhook/admission
|
|||
sigs.k8s.io/controller-runtime/pkg/webhook/conversion
|
||||
sigs.k8s.io/controller-runtime/pkg/webhook/internal/certwatcher
|
||||
sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics
|
||||
# sigs.k8s.io/controller-tools v0.3.1-0.20200617211605-651903477185 => sigs.k8s.io/controller-tools v0.3.0
|
||||
# sigs.k8s.io/controller-tools v0.5.0 => sigs.k8s.io/controller-tools v0.5.0
|
||||
## explicit
|
||||
sigs.k8s.io/controller-tools/cmd/controller-gen
|
||||
sigs.k8s.io/controller-tools/pkg/crd
|
||||
|
@ -1869,6 +1869,6 @@ sigs.k8s.io/yaml
|
|||
# sigs.k8s.io/cluster-api-provider-gcp => github.com/openshift/cluster-api-provider-gcp v0.0.1-0.20201002153134-a0fc9aa4ce81
|
||||
# sigs.k8s.io/cluster-api-provider-openstack => github.com/openshift/cluster-api-provider-openstack v0.0.0-20201002114634-3622a0ce6b56
|
||||
# sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.6.4
|
||||
# sigs.k8s.io/controller-tools => sigs.k8s.io/controller-tools v0.3.0
|
||||
# sigs.k8s.io/controller-tools => sigs.k8s.io/controller-tools v0.5.0
|
||||
# github.com/satori/go.uuid => github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b
|
||||
# github.com/satori/uuid => github.com/satori/uuid v1.2.1-0.20181028125025-b2ce2384e17b
|
||||
|
|
|
@ -18,7 +18,9 @@ package crd
|
|||
|
||||
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"
|
||||
|
@ -31,6 +33,9 @@ import (
|
|||
"sigs.k8s.io/controller-tools/pkg/version"
|
||||
)
|
||||
|
||||
// The default CustomResourceDefinition version to generate.
|
||||
const defaultVersion = "v1"
|
||||
|
||||
// +controllertools:marker:generateHelp
|
||||
|
||||
// Generator generates CustomResourceDefinition objects.
|
||||
|
@ -53,6 +58,16 @@ type Generator struct {
|
|||
// 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.
|
||||
|
@ -61,7 +76,7 @@ type Generator struct {
|
|||
MaxDescLen *int `marker:",optional"`
|
||||
|
||||
// CRDVersions specifies the target API versions of the CRD type itself to
|
||||
// generate. Defaults to v1beta1.
|
||||
// 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.
|
||||
|
@ -71,6 +86,9 @@ type Generator struct {
|
|||
CRDVersions []string `marker:"crdVersions,optional"`
|
||||
}
|
||||
|
||||
func (Generator) CheckFilter() loader.NodeFilter {
|
||||
return filterTypesForCRDs
|
||||
}
|
||||
func (Generator) RegisterMarkers(into *markers.Registry) error {
|
||||
return crdmarkers.Register(into)
|
||||
}
|
||||
|
@ -78,6 +96,8 @@ func (g Generator) Generate(ctx *genall.GenerationContext) error {
|
|||
parser := &Parser{
|
||||
Collector: ctx.Collector,
|
||||
Checker: ctx.Checker,
|
||||
// Perform defaulting here to avoid ambiguity later
|
||||
AllowDangerousTypes: g.AllowDangerousTypes != nil && *g.AllowDangerousTypes == true,
|
||||
}
|
||||
|
||||
AddKnownTypes(parser)
|
||||
|
@ -101,7 +121,7 @@ func (g Generator) Generate(ctx *genall.GenerationContext) error {
|
|||
crdVersions := g.CRDVersions
|
||||
|
||||
if len(crdVersions) == 0 {
|
||||
crdVersions = []string{"v1beta1"}
|
||||
crdVersions = []string{defaultVersion}
|
||||
}
|
||||
|
||||
for groupKind := range kubeKinds {
|
||||
|
@ -141,6 +161,15 @@ func (g Generator) Generate(ctx *genall.GenerationContext) error {
|
|||
}
|
||||
|
||||
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))
|
||||
}
|
||||
var fileName string
|
||||
if i == 0 {
|
||||
fileName = fmt.Sprintf("%s_%s.yaml", crdRaw.Spec.Group, crdRaw.Spec.Names.Plural)
|
||||
|
@ -156,6 +185,90 @@ func (g Generator) Generate(ctx *genall.GenerationContext) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func removeDescriptionFromMetadata(crd *apiext.CustomResourceDefinition) {
|
||||
for _, versionSpec := range crd.Spec.Versions {
|
||||
if versionSpec.Schema != nil {
|
||||
removeDescriptionFromMetadataProps(versionSpec.Schema.OpenAPIV3Schema)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeDescriptionFromMetadataProps(v *apiext.JSONSchemaProps) {
|
||||
if m, ok := v.Properties["metadata"]; ok {
|
||||
meta := &m
|
||||
if meta.Description != "" {
|
||||
meta.Description = ""
|
||||
v.Properties["metadata"] = m
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
@ -262,3 +375,22 @@ func FindKubeKinds(parser *Parser, metav1Pkg *loader.Package) map[schema.GroupKi
|
|||
|
||||
return kubeKinds
|
||||
}
|
||||
|
||||
// filterTypesForCRDs filters out all nodes that aren't used in CRD generation,
|
||||
// like interfaces and struct fields without JSON tag.
|
||||
func filterTypesForCRDs(node ast.Node) bool {
|
||||
switch node := node.(type) {
|
||||
case *ast.InterfaceType:
|
||||
// skip interfaces, we never care about references in them
|
||||
return false
|
||||
case *ast.StructType:
|
||||
return true
|
||||
case *ast.Field:
|
||||
_, hasTag := loader.ParseAstTag(node.Tag).Lookup("json")
|
||||
// fields without JSON tags mean we have custom serialization,
|
||||
// so only visit fields with tags.
|
||||
return hasTag
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,14 @@ import (
|
|||
// 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).
|
||||
var KnownPackages = map[string]PackageOverride{
|
||||
"k8s.io/api/core/v1": func(p *Parser, pkg *loader.Package) {
|
||||
// Explicit defaulting for the corev1.Protocol type in lieu of https://github.com/kubernetes/enhancements/pull/1928
|
||||
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) {
|
||||
// ObjectMeta is managed by the Kubernetes API server, so no need to
|
||||
|
|
|
@ -26,6 +26,10 @@ import (
|
|||
"sigs.k8s.io/controller-tools/pkg/markers"
|
||||
)
|
||||
|
||||
const (
|
||||
SchemalessName = "kubebuilder:validation:Schemaless"
|
||||
)
|
||||
|
||||
// ValidationMarkers lists all available markers that affect CRD schema generation,
|
||||
// except for the few that don't make sense as type-level markers (see FieldOnlyMarkers).
|
||||
// All markers start with `+kubebuilder:validation:`, and continue with their type name.
|
||||
|
@ -40,6 +44,8 @@ var ValidationMarkers = mustMakeAllWithPrefix("kubebuilder:validation", markers.
|
|||
ExclusiveMaximum(false),
|
||||
ExclusiveMinimum(false),
|
||||
MultipleOf(0),
|
||||
MinProperties(0),
|
||||
MaxProperties(0),
|
||||
|
||||
// string markers
|
||||
|
||||
|
@ -78,10 +84,21 @@ var FieldOnlyMarkers = []*definitionWithHelp{
|
|||
must(markers.MakeAnyTypeDefinition("kubebuilder:default", markers.DescribesField, Default{})).
|
||||
WithHelp(Default{}.Help()),
|
||||
|
||||
must(markers.MakeDefinition("kubebuilder:pruning:PreserveUnknownFields", markers.DescribesField, XPreserveUnknownFields{})).
|
||||
WithHelp(XPreserveUnknownFields{}.Help()),
|
||||
must(markers.MakeDefinition("kubebuilder:validation:EmbeddedResource", markers.DescribesField, XEmbeddedResource{})).
|
||||
WithHelp(XEmbeddedResource{}.Help()),
|
||||
|
||||
must(markers.MakeDefinition(SchemalessName, markers.DescribesField, Schemaless{})).
|
||||
WithHelp(Schemaless{}.Help()),
|
||||
}
|
||||
|
||||
// ValidationIshMarkers are field-and-type markers that don't fall under the
|
||||
// :validation: prefix, and/or don't have a name that directly matches their
|
||||
// type.
|
||||
var ValidationIshMarkers = []*definitionWithHelp{
|
||||
must(markers.MakeDefinition("kubebuilder:pruning:PreserveUnknownFields", markers.DescribesField, XPreserveUnknownFields{})).
|
||||
WithHelp(XPreserveUnknownFields{}.Help()),
|
||||
must(markers.MakeDefinition("kubebuilder:pruning:PreserveUnknownFields", markers.DescribesType, XPreserveUnknownFields{})).
|
||||
WithHelp(XPreserveUnknownFields{}.Help()),
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -99,6 +116,7 @@ func init() {
|
|||
}
|
||||
|
||||
AllDefinitions = append(AllDefinitions, FieldOnlyMarkers...)
|
||||
AllDefinitions = append(AllDefinitions, ValidationIshMarkers...)
|
||||
}
|
||||
|
||||
// +controllertools:marker:generateHelp:category="CRD validation"
|
||||
|
@ -106,7 +124,7 @@ func init() {
|
|||
type Maximum int
|
||||
|
||||
// +controllertools:marker:generateHelp:category="CRD validation"
|
||||
// Minimum specifies the minimum numeric value that this field can have.
|
||||
// Minimum specifies the minimum numeric value that this field can have. Negative integers are supported.
|
||||
type Minimum int
|
||||
|
||||
// +controllertools:marker:generateHelp:category="CRD validation"
|
||||
|
@ -145,6 +163,14 @@ type MinItems int
|
|||
// UniqueItems specifies that all items in this list must be unique.
|
||||
type UniqueItems bool
|
||||
|
||||
// +controllertools:marker:generateHelp:category="CRD validation"
|
||||
// MaxProperties restricts the number of keys in an object
|
||||
type MaxProperties int
|
||||
|
||||
// +controllertools:marker:generateHelp:category="CRD validation"
|
||||
// MinProperties restricts the number of keys in an object
|
||||
type MinProperties int
|
||||
|
||||
// +controllertools:marker:generateHelp:category="CRD validation"
|
||||
// Enum specifies that this (scalar) field is restricted to the *exact* values specified here.
|
||||
type Enum []interface{}
|
||||
|
@ -191,6 +217,10 @@ type Default struct {
|
|||
// if nested properties or additionalProperties are specified in the schema.
|
||||
// This can either be true or undefined. False
|
||||
// is forbidden.
|
||||
//
|
||||
// NB: The kubebuilder:validation:XPreserveUnknownFields variant is deprecated
|
||||
// in favor of the kubebuilder:pruning:PreserveUnknownFields variant. They function
|
||||
// identically.
|
||||
type XPreserveUnknownFields struct{}
|
||||
|
||||
// +controllertools:marker:generateHelp:category="CRD validation"
|
||||
|
@ -202,6 +232,16 @@ type XPreserveUnknownFields struct{}
|
|||
// field, yet it is possible. This can be combined with PreserveUnknownFields.
|
||||
type XEmbeddedResource struct{}
|
||||
|
||||
// +controllertools:marker:generateHelp:category="CRD validation"
|
||||
// Schemaless marks a field as being a schemaless object.
|
||||
//
|
||||
// Schemaless objects are not introspected, so you must provide
|
||||
// any type and validation information yourself. One use for this
|
||||
// tag is for embedding fields that hold JSONSchema typed objects.
|
||||
// Because this field disables all type checking, it is recommended
|
||||
// to be used only as a last resort.
|
||||
type Schemaless struct{}
|
||||
|
||||
func (m Maximum) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
|
||||
if schema.Type != "integer" {
|
||||
return fmt.Errorf("must apply maximum to an integer")
|
||||
|
@ -289,6 +329,24 @@ func (m UniqueItems) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m MinProperties) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
|
||||
if schema.Type != "object" {
|
||||
return fmt.Errorf("must apply minproperties to an object")
|
||||
}
|
||||
val := int64(m)
|
||||
schema.MinProperties = &val
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m MaxProperties) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
|
||||
if schema.Type != "object" {
|
||||
return fmt.Errorf("must apply maxproperties to an object")
|
||||
}
|
||||
val := int64(m)
|
||||
schema.MaxProperties = &val
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m Enum) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
|
||||
// TODO(directxman12): this is a bit hacky -- we should
|
||||
// probably support AnyType better + using the schema structure
|
||||
|
|
37
vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/zz_generated.markerhelp.go
сгенерированный
поставляемый
37
vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/zz_generated.markerhelp.go
сгенерированный
поставляемый
|
@ -139,6 +139,17 @@ func (MaxLength) Help() *markers.DefinitionHelp {
|
|||
}
|
||||
}
|
||||
|
||||
func (MaxProperties) Help() *markers.DefinitionHelp {
|
||||
return &markers.DefinitionHelp{
|
||||
Category: "CRD validation",
|
||||
DetailedHelp: markers.DetailedHelp{
|
||||
Summary: "restricts the number of keys in an object",
|
||||
Details: "",
|
||||
},
|
||||
FieldHelp: map[string]markers.DetailedHelp{},
|
||||
}
|
||||
}
|
||||
|
||||
func (Maximum) Help() *markers.DefinitionHelp {
|
||||
return &markers.DefinitionHelp{
|
||||
Category: "CRD validation",
|
||||
|
@ -172,11 +183,22 @@ func (MinLength) Help() *markers.DefinitionHelp {
|
|||
}
|
||||
}
|
||||
|
||||
func (MinProperties) Help() *markers.DefinitionHelp {
|
||||
return &markers.DefinitionHelp{
|
||||
Category: "CRD validation",
|
||||
DetailedHelp: markers.DetailedHelp{
|
||||
Summary: "restricts the number of keys in an object",
|
||||
Details: "",
|
||||
},
|
||||
FieldHelp: map[string]markers.DetailedHelp{},
|
||||
}
|
||||
}
|
||||
|
||||
func (Minimum) Help() *markers.DefinitionHelp {
|
||||
return &markers.DefinitionHelp{
|
||||
Category: "CRD validation",
|
||||
DetailedHelp: markers.DetailedHelp{
|
||||
Summary: "specifies the minimum numeric value that this field can have.",
|
||||
Summary: "specifies the minimum numeric value that this field can have. Negative integers are supported.",
|
||||
Details: "",
|
||||
},
|
||||
FieldHelp: map[string]markers.DetailedHelp{},
|
||||
|
@ -284,6 +306,17 @@ func (Resource) Help() *markers.DefinitionHelp {
|
|||
}
|
||||
}
|
||||
|
||||
func (Schemaless) Help() *markers.DefinitionHelp {
|
||||
return &markers.DefinitionHelp{
|
||||
Category: "CRD validation",
|
||||
DetailedHelp: markers.DetailedHelp{
|
||||
Summary: "marks a field as being a schemaless object. ",
|
||||
Details: "Schemaless objects are not introspected, so you must provide any type and validation information yourself. One use for this tag is for embedding fields that hold JSONSchema typed objects. Because this field disables all type checking, it is recommended to be used only as a last resort.",
|
||||
},
|
||||
FieldHelp: map[string]markers.DetailedHelp{},
|
||||
}
|
||||
}
|
||||
|
||||
func (SkipVersion) Help() *markers.DefinitionHelp {
|
||||
return &markers.DefinitionHelp{
|
||||
Category: "CRD",
|
||||
|
@ -401,7 +434,7 @@ func (XPreserveUnknownFields) Help() *markers.DefinitionHelp {
|
|||
Category: "CRD processing",
|
||||
DetailedHelp: markers.DetailedHelp{
|
||||
Summary: "PreserveUnknownFields stops the apiserver from pruning fields which are not specified. ",
|
||||
Details: "By default the apiserver drops unknown fields from the request payload during the decoding step. This marker stops the API server from doing so. It affects fields recursively, but switches back to normal pruning behaviour if nested properties or additionalProperties are specified in the schema. This can either be true or undefined. False is forbidden.",
|
||||
Details: "By default the apiserver drops unknown fields from the request payload during the decoding step. This marker stops the API server from doing so. It affects fields recursively, but switches back to normal pruning behaviour if nested properties or additionalProperties are specified in the schema. This can either be true or undefined. False is forbidden. \n NB: The kubebuilder:validation:XPreserveUnknownFields variant is deprecated in favor of the kubebuilder:pruning:PreserveUnknownFields variant. They function identically.",
|
||||
},
|
||||
FieldHelp: map[string]markers.DetailedHelp{},
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package crd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
|
||||
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
@ -73,6 +72,20 @@ type Parser struct {
|
|||
packages map[*loader.Package]struct{}
|
||||
|
||||
flattener *Flattener
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func (p *Parser) init() {
|
||||
|
@ -162,7 +175,7 @@ func (p *Parser) NeedSchemaFor(typ TypeIdent) {
|
|||
// avoid tripping recursive schemata, like ManagedFields, by adding an empty WIP schema
|
||||
p.Schemata[typ] = apiext.JSONSchemaProps{}
|
||||
|
||||
schemaCtx := newSchemaContext(typ.Package, p)
|
||||
schemaCtx := newSchemaContext(typ.Package, p, p.AllowDangerousTypes)
|
||||
ctxForInfo := schemaCtx.ForInfo(info)
|
||||
|
||||
pkgMarkers, err := markers.PackageMarkers(p.Collector, typ.Package)
|
||||
|
@ -202,7 +215,7 @@ func (p *Parser) AddPackage(pkg *loader.Package) {
|
|||
return
|
||||
}
|
||||
p.indexTypes(pkg)
|
||||
p.Checker.Check(pkg, filterTypesForCRDs)
|
||||
p.Checker.Check(pkg)
|
||||
p.packages[pkg] = struct{}{}
|
||||
}
|
||||
|
||||
|
@ -222,22 +235,3 @@ func (p *Parser) NeedPackage(pkg *loader.Package) {
|
|||
}
|
||||
p.AddPackage(pkg)
|
||||
}
|
||||
|
||||
// filterTypesForCRDs filters out all nodes that aren't used in CRD generation,
|
||||
// like interfaces and struct fields without JSON tag.
|
||||
func filterTypesForCRDs(node ast.Node) bool {
|
||||
switch node := node.(type) {
|
||||
case *ast.InterfaceType:
|
||||
// skip interfaces, we never care about references in them
|
||||
return false
|
||||
case *ast.StructType:
|
||||
return true
|
||||
case *ast.Field:
|
||||
_, hasTag := loader.ParseAstTag(node.Tag).Lookup("json")
|
||||
// fields without JSON tags mean we have custom serialization,
|
||||
// so only visit fields with tags.
|
||||
return hasTag
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"strings"
|
||||
|
||||
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
crdmarkers "sigs.k8s.io/controller-tools/pkg/crd/markers"
|
||||
|
||||
"sigs.k8s.io/controller-tools/pkg/loader"
|
||||
"sigs.k8s.io/controller-tools/pkg/markers"
|
||||
|
@ -67,15 +68,18 @@ type schemaContext struct {
|
|||
|
||||
schemaRequester schemaRequester
|
||||
PackageMarkers markers.MarkerValues
|
||||
|
||||
allowDangerousTypes bool
|
||||
}
|
||||
|
||||
// newSchemaContext constructs a new schemaContext for the given package and schema requester.
|
||||
// It must have type info added before use via ForInfo.
|
||||
func newSchemaContext(pkg *loader.Package, req schemaRequester) *schemaContext {
|
||||
func newSchemaContext(pkg *loader.Package, req schemaRequester, allowDangerousTypes bool) *schemaContext {
|
||||
pkg.NeedTypesInfo()
|
||||
return &schemaContext{
|
||||
pkg: pkg,
|
||||
schemaRequester: req,
|
||||
pkg: pkg,
|
||||
schemaRequester: req,
|
||||
allowDangerousTypes: allowDangerousTypes,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,9 +87,10 @@ func newSchemaContext(pkg *loader.Package, req schemaRequester) *schemaContext {
|
|||
// as this one, except with the given type information.
|
||||
func (c *schemaContext) ForInfo(info *markers.TypeInfo) *schemaContext {
|
||||
return &schemaContext{
|
||||
pkg: c.pkg,
|
||||
info: info,
|
||||
schemaRequester: c.schemaRequester,
|
||||
pkg: c.pkg,
|
||||
info: info,
|
||||
schemaRequester: c.schemaRequester,
|
||||
allowDangerousTypes: c.allowDangerousTypes,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,7 +205,7 @@ func localNamedToSchema(ctx *schemaContext, ident *ast.Ident) *apiext.JSONSchema
|
|||
return &apiext.JSONSchemaProps{}
|
||||
}
|
||||
if basicInfo, isBasic := typeInfo.(*types.Basic); isBasic {
|
||||
typ, fmt, err := builtinToType(basicInfo)
|
||||
typ, fmt, err := builtinToType(basicInfo, ctx.allowDangerousTypes)
|
||||
if err != nil {
|
||||
ctx.pkg.AddError(loader.ErrFromNode(err, ident))
|
||||
}
|
||||
|
@ -297,6 +302,8 @@ func mapToSchema(ctx *schemaContext, mapType *ast.MapType) *apiext.JSONSchemaPro
|
|||
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)
|
||||
default:
|
||||
ctx.pkg.AddError(loader.ErrFromNode(fmt.Errorf("map values must be a named type, not %T", mapType.Value), mapType.Value))
|
||||
return &apiext.JSONSchemaProps{}
|
||||
|
@ -372,7 +379,12 @@ func structToSchema(ctx *schemaContext, structType *ast.StructType) *apiext.JSON
|
|||
}
|
||||
}
|
||||
|
||||
propSchema := typeToSchema(ctx.ForInfo(&markers.TypeInfo{}), field.RawField.Type)
|
||||
var propSchema *apiext.JSONSchemaProps
|
||||
if field.Markers.Get(crdmarkers.SchemalessName) != nil {
|
||||
propSchema = &apiext.JSONSchemaProps{}
|
||||
} else {
|
||||
propSchema = typeToSchema(ctx.ForInfo(&markers.TypeInfo{}), field.RawField.Type)
|
||||
}
|
||||
propSchema.Description = field.Doc
|
||||
|
||||
applyMarkers(ctx, field.Markers, propSchema, field.RawField)
|
||||
|
@ -390,8 +402,8 @@ func structToSchema(ctx *schemaContext, structType *ast.StructType) *apiext.JSON
|
|||
|
||||
// builtinToType converts builtin basic types to their equivalent JSON schema form.
|
||||
// It *only* handles types allowed by the kubernetes API standards. Floats are not
|
||||
// allowed.
|
||||
func builtinToType(basic *types.Basic) (typ string, format string, err error) {
|
||||
// allowed unless allowDangerousTypes is true
|
||||
func builtinToType(basic *types.Basic, allowDangerousTypes bool) (typ string, format string, err error) {
|
||||
// NB(directxman12): formats from OpenAPI v3 are slightly different than those defined
|
||||
// in JSONSchema. This'll use the OpenAPI v3 ones, since they're useful for bounding our
|
||||
// non-string types.
|
||||
|
@ -403,6 +415,8 @@ func builtinToType(basic *types.Basic) (typ string, format string, err error) {
|
|||
typ = "string"
|
||||
case basicInfo&types.IsInteger != 0:
|
||||
typ = "integer"
|
||||
case basicInfo&types.IsFloat != 0 && allowDangerousTypes:
|
||||
typ = "number"
|
||||
default:
|
||||
// NB(directxman12): floats are *NOT* allowed in kubernetes APIs
|
||||
return "", "", fmt.Errorf("unsupported type %q", basic.String())
|
||||
|
|
|
@ -27,6 +27,10 @@ type SchemaVisitor interface {
|
|||
// 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
|
||||
}
|
||||
|
||||
|
@ -44,16 +48,37 @@ type schemaWalker struct {
|
|||
visitor SchemaVisitor
|
||||
}
|
||||
|
||||
// walkSchema walks the given schema, saving modifications made by the
|
||||
// visitor (this is as simple as passing a pointer in most cases,
|
||||
// but special care needs to be taken to persist with maps).
|
||||
// walkSchema walks the given schema, saving modifications made by the visitor
|
||||
// (this is as simple as passing a pointer in most cases, but special care
|
||||
// needs to be taken to persist with maps). It also visits referenced
|
||||
// schemata, dealing with circular references appropriately. The returned
|
||||
// visitor will be used to visit all "children" of the current schema, followed
|
||||
// by a nil schema with the returned visitor to mark completion. If a nil visitor
|
||||
// is returned, traversal will no continue into the children of the current schema.
|
||||
func (w schemaWalker) walkSchema(schema *apiext.JSONSchemaProps) {
|
||||
subVisitor := w.visitor.Visit(schema)
|
||||
if subVisitor == nil {
|
||||
return
|
||||
// Walk a potential chain of schema references, keeping track of seen
|
||||
// references to avoid circular references
|
||||
subVisitor := w.visitor
|
||||
seenRefs := map[string]bool{}
|
||||
if schema.Ref != nil {
|
||||
seenRefs[*schema.Ref] = true
|
||||
}
|
||||
defer subVisitor.Visit(nil)
|
||||
for {
|
||||
subVisitor = subVisitor.Visit(schema)
|
||||
if subVisitor == nil {
|
||||
return
|
||||
}
|
||||
// mark completion of the visitor
|
||||
defer subVisitor.Visit(nil)
|
||||
|
||||
// Break if schema is not a reference or a cycle is detected
|
||||
if schema.Ref == nil || len(*schema.Ref) == 0 || seenRefs[*schema.Ref] {
|
||||
break
|
||||
}
|
||||
seenRefs[*schema.Ref] = true
|
||||
}
|
||||
|
||||
// walk sub-schemata
|
||||
subWalker := schemaWalker{visitor: subVisitor}
|
||||
if schema.Items != nil {
|
||||
subWalker.walkPtr(schema.Items.Schema)
|
||||
|
@ -81,6 +106,8 @@ func (w schemaWalker) walkSchema(schema *apiext.JSONSchemaProps) {
|
|||
// walkMap walks over values of the given map, saving changes to them.
|
||||
func (w schemaWalker) walkMap(defs map[string]apiext.JSONSchemaProps) {
|
||||
for name, def := range defs {
|
||||
// this is iter var reference is because we immediately preseve it below
|
||||
//nolint:gosec
|
||||
w.walkSchema(&def)
|
||||
// make sure the edits actually go through since we can't
|
||||
// take a reference to the value in the map
|
||||
|
|
|
@ -55,7 +55,7 @@ func (p *Parser) NeedCRDFor(groupKind schema.GroupKind, maxDescLen *int) {
|
|||
packages = append(packages, pkg)
|
||||
}
|
||||
|
||||
defaultPlural := flect.Pluralize(strings.ToLower(groupKind.Kind))
|
||||
defaultPlural := strings.ToLower(flect.Pluralize(groupKind.Kind))
|
||||
crd := apiext.CustomResourceDefinition{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: apiext.SchemeGroupVersion.String(),
|
||||
|
|
6
vendor/sigs.k8s.io/controller-tools/pkg/crd/zz_generated.markerhelp.go
сгенерированный
поставляемый
6
vendor/sigs.k8s.io/controller-tools/pkg/crd/zz_generated.markerhelp.go
сгенерированный
поставляемый
|
@ -40,12 +40,16 @@ func (Generator) Help() *markers.DefinitionHelp {
|
|||
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": markers.DetailedHelp{
|
||||
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",
|
||||
},
|
||||
"MaxDescLen": markers.DetailedHelp{
|
||||
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.",
|
||||
},
|
||||
"CRDVersions": markers.DetailedHelp{
|
||||
Summary: "specifies the target API versions of the CRD type itself to generate. Defaults to v1beta1. ",
|
||||
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+).",
|
||||
},
|
||||
},
|
||||
|
|
|
@ -58,6 +58,14 @@ type Generator struct {
|
|||
Year string `marker:",optional"`
|
||||
}
|
||||
|
||||
func (Generator) CheckFilter() loader.NodeFilter {
|
||||
return func(node ast.Node) bool {
|
||||
// ignore interfaces
|
||||
_, isIface := node.(*ast.InterfaceType)
|
||||
return !isIface
|
||||
}
|
||||
}
|
||||
|
||||
func (Generator) RegisterMarkers(into *markers.Registry) error {
|
||||
if err := markers.RegisterAll(into,
|
||||
enablePkgMarker, legacyEnablePkgMarker, enableTypeMarker,
|
||||
|
@ -144,7 +152,7 @@ func (d Generator) Generate(ctx *genall.GenerationContext) error {
|
|||
}
|
||||
|
||||
for _, root := range ctx.Roots {
|
||||
outContents := objGenCtx.GenerateForPackage(root)
|
||||
outContents := objGenCtx.generateForPackage(root)
|
||||
if outContents == nil {
|
||||
continue
|
||||
}
|
||||
|
@ -186,21 +194,17 @@ import (
|
|||
|
||||
}
|
||||
|
||||
// GenerateForPackage generates DeepCopy and runtime.Object implementations for
|
||||
// generateForPackage generates DeepCopy and runtime.Object implementations for
|
||||
// types in the given package, writing the formatted result to given writer.
|
||||
// May return nil if source could not be generated.
|
||||
func (ctx *ObjectGenCtx) GenerateForPackage(root *loader.Package) []byte {
|
||||
func (ctx *ObjectGenCtx) generateForPackage(root *loader.Package) []byte {
|
||||
allTypes, err := enabledOnPackage(ctx.Collector, root)
|
||||
if err != nil {
|
||||
root.AddError(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx.Checker.Check(root, func(node ast.Node) bool {
|
||||
// ignore interfaces
|
||||
_, isIface := node.(*ast.InterfaceType)
|
||||
return !isIface
|
||||
})
|
||||
ctx.Checker.Check(root)
|
||||
|
||||
root.NeedTypesInfo()
|
||||
|
||||
|
|
|
@ -197,7 +197,7 @@ func (n *namingInfo) Syntax(basePkg *loader.Package, imports *importsList) strin
|
|||
(&namingInfo{typeInfo: typeInfo.Key()}).Syntax(basePkg, imports),
|
||||
(&namingInfo{typeInfo: typeInfo.Elem()}).Syntax(basePkg, imports))
|
||||
default:
|
||||
basePkg.AddError(fmt.Errorf("name requested for invalid type %s", typeInfo))
|
||||
basePkg.AddError(fmt.Errorf("name requested for invalid type: %s", typeInfo))
|
||||
return typeInfo.String()
|
||||
}
|
||||
}
|
||||
|
@ -215,7 +215,7 @@ type copyMethodMaker struct {
|
|||
func (c *copyMethodMaker) GenerateMethodsFor(root *loader.Package, info *markers.TypeInfo) {
|
||||
typeInfo := root.TypesInfo.TypeOf(info.RawSpec.Name)
|
||||
if typeInfo == types.Typ[types.Invalid] {
|
||||
root.AddError(loader.ErrFromNode(fmt.Errorf("unknown type %s", info.Name), info.RawSpec))
|
||||
root.AddError(loader.ErrFromNode(fmt.Errorf("unknown type: %s", info.Name), info.RawSpec))
|
||||
}
|
||||
|
||||
// figure out if we need to use a pointer receiver -- most types get a pointer receiver,
|
||||
|
@ -293,13 +293,18 @@ func (c *copyMethodMaker) genDeepCopyIntoBlock(actualName *namingInfo, typeInfo
|
|||
|
||||
switch last := last.(type) {
|
||||
case *types.Basic:
|
||||
// basic types themselves can be "shallow" copied, so all we need
|
||||
// to do is check if our *actual* type (not the underlying one) has
|
||||
// a custom method implemented.
|
||||
if hasMethod, _ := hasDeepCopyMethod(c.pkg, typeInfo); hasMethod {
|
||||
c.Line("*out = in.DeepCopy()")
|
||||
switch last.Kind() {
|
||||
case types.Invalid, types.UnsafePointer:
|
||||
c.pkg.AddError(fmt.Errorf("invalid type: %s", last))
|
||||
default:
|
||||
// basic types themselves can be "shallow" copied, so all we need
|
||||
// to do is check if our *actual* type (not the underlying one) has
|
||||
// a custom method implemented.
|
||||
if hasMethod, _ := hasDeepCopyMethod(c.pkg, typeInfo); hasMethod {
|
||||
c.Line("*out = in.DeepCopy()")
|
||||
}
|
||||
c.Line("*out = *in")
|
||||
}
|
||||
c.Line("*out = *in")
|
||||
case *types.Map:
|
||||
c.genMapDeepCopy(actualName, last)
|
||||
case *types.Slice:
|
||||
|
@ -312,7 +317,7 @@ func (c *copyMethodMaker) genDeepCopyIntoBlock(actualName *namingInfo, typeInfo
|
|||
// handled via the above loop, should never happen
|
||||
c.pkg.AddError(fmt.Errorf("interface type %s encountered directly, invalid condition", last))
|
||||
default:
|
||||
c.pkg.AddError(fmt.Errorf("invalid type %s", last))
|
||||
c.pkg.AddError(fmt.Errorf("invalid type: %s", last))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -322,7 +327,7 @@ func (c *copyMethodMaker) genMapDeepCopy(actualName *namingInfo, mapType *types.
|
|||
// maps *must* have shallow-copiable types, since we just iterate
|
||||
// through the keys, only trying to deepcopy the values.
|
||||
if !fineToShallowCopy(mapType.Key()) {
|
||||
c.pkg.AddError(fmt.Errorf("invalid map key type %s", mapType.Key()))
|
||||
c.pkg.AddError(fmt.Errorf("invalid map key type: %s", mapType.Key()))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -383,7 +388,7 @@ func (c *copyMethodMaker) genMapDeepCopy(actualName *namingInfo, mapType *types.
|
|||
// structs will have deepcopy generated for them, so use that
|
||||
c.Line("(*out)[key] = *val.DeepCopy()")
|
||||
default:
|
||||
c.pkg.AddError(fmt.Errorf("invalid map value type %s", underlyingElem))
|
||||
c.pkg.AddError(fmt.Errorf("invalid map value type: %s", underlyingElem))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -425,7 +430,7 @@ func (c *copyMethodMaker) genSliceDeepCopy(actualName *namingInfo, sliceType *ty
|
|||
// structs will always have deepcopy
|
||||
c.Linef("(*in)[i].DeepCopyInto(&(*out)[i])")
|
||||
default:
|
||||
c.pkg.AddError(fmt.Errorf("invalid slice element type %s", underlyingElem))
|
||||
c.pkg.AddError(fmt.Errorf("invalid slice element type: %s", underlyingElem))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -483,7 +488,13 @@ func (c *copyMethodMaker) genStructDeepCopy(_ *namingInfo, structType *types.Str
|
|||
// otherwise...
|
||||
switch underlyingField := underlyingField.(type) {
|
||||
case *types.Basic:
|
||||
// nothing to do, initial assignment copied this
|
||||
switch underlyingField.Kind() {
|
||||
case types.Invalid, types.UnsafePointer:
|
||||
c.pkg.AddError(loader.ErrFromNode(fmt.Errorf("invalid field type: %s", underlyingField), field))
|
||||
return
|
||||
default:
|
||||
// nothing to do, initial assignment copied this
|
||||
}
|
||||
case *types.Struct:
|
||||
if fineToShallowCopy(field.Type()) {
|
||||
c.Linef("out.%[1]s = in.%[1]s", field.Name())
|
||||
|
@ -491,7 +502,7 @@ func (c *copyMethodMaker) genStructDeepCopy(_ *namingInfo, structType *types.Str
|
|||
c.Linef("in.%[1]s.DeepCopyInto(&out.%[1]s)", field.Name())
|
||||
}
|
||||
default:
|
||||
c.pkg.AddError(fmt.Errorf("invalid field type %s", underlyingField))
|
||||
c.pkg.AddError(loader.ErrFromNode(fmt.Errorf("invalid field type: %s", underlyingField), field))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -542,7 +553,7 @@ func (c *copyMethodMaker) genPointerDeepCopy(_ *namingInfo, pointerType *types.P
|
|||
c.Linef("*out = new(%[1]s)", (&namingInfo{typeInfo: pointerType.Elem()}).Syntax(c.pkg, c.importsList))
|
||||
c.Line("(*in).DeepCopyInto(*out)")
|
||||
default:
|
||||
c.pkg.AddError(fmt.Errorf("invalid pointer element type %s", underlyingElem))
|
||||
c.pkg.AddError(fmt.Errorf("invalid pointer element type: %s", underlyingElem))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -602,7 +613,7 @@ func shouldBeCopied(pkg *loader.Package, info *markers.TypeInfo) bool {
|
|||
|
||||
typeInfo := pkg.TypesInfo.TypeOf(info.RawSpec.Name)
|
||||
if typeInfo == types.Typ[types.Invalid] {
|
||||
pkg.AddError(loader.ErrFromNode(fmt.Errorf("unknown type %s", info.Name), info.RawSpec))
|
||||
pkg.AddError(loader.ErrFromNode(fmt.Errorf("unknown type: %s", info.Name), info.RawSpec))
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -735,8 +746,14 @@ func eventualUnderlyingType(typeInfo types.Type) types.Type {
|
|||
func fineToShallowCopy(typeInfo types.Type) bool {
|
||||
switch typeInfo := typeInfo.(type) {
|
||||
case *types.Basic:
|
||||
// basic types (int, string, etc) are always fine to shallow-copy
|
||||
return true
|
||||
// basic types (int, string, etc) are always fine to shallow-copy,
|
||||
// except for Invalid and UnsafePointer, which can't be copied at all.
|
||||
switch typeInfo.Kind() {
|
||||
case types.Invalid, types.UnsafePointer:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
case *types.Named:
|
||||
// aliases are fine to shallow-copy as long as they resolve to a shallow-copyable type
|
||||
return fineToShallowCopy(typeInfo.Underlying())
|
||||
|
|
|
@ -46,6 +46,32 @@ func (g Generators) RegisterMarkers(reg *markers.Registry) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// CheckFilters returns the set of NodeFilters for all Generators that
|
||||
// implement NeedsTypeChecking.
|
||||
func (g Generators) CheckFilters() []loader.NodeFilter {
|
||||
var filters []loader.NodeFilter
|
||||
for _, gen := range g {
|
||||
withFilter, needsChecking := (*gen).(NeedsTypeChecking)
|
||||
if !needsChecking {
|
||||
continue
|
||||
}
|
||||
filters = append(filters, withFilter.CheckFilter())
|
||||
}
|
||||
return filters
|
||||
}
|
||||
|
||||
// NeedsTypeChecking indicates that a particular generator needs & has opinions
|
||||
// on typechecking. If this is not implemented, a generator will be given a
|
||||
// context with a nil typechecker.
|
||||
type NeedsTypeChecking interface {
|
||||
// CheckFilter indicates the loader.NodeFilter (if any) that should be used
|
||||
// to prune out unused types/packages when type-checking (nodes for which
|
||||
// the filter returns true are considered "interesting"). This filter acts
|
||||
// as a baseline -- all types the pass through this filter will be checked,
|
||||
// but more than that may also be checked due to other generators' filters.
|
||||
CheckFilter() loader.NodeFilter
|
||||
}
|
||||
|
||||
// Generator knows how to register some set of markers, and then produce
|
||||
// output artifacts based on loaded code containing those markers,
|
||||
// sharing common loaded data.
|
||||
|
@ -144,7 +170,9 @@ func (g Generators) ForRoots(rootPaths ...string) (*Runtime, error) {
|
|||
},
|
||||
Roots: roots,
|
||||
InputRule: InputFromFileSystem,
|
||||
Checker: &loader.TypeChecker{},
|
||||
Checker: &loader.TypeChecker{
|
||||
NodeFilters: g.CheckFilters(),
|
||||
},
|
||||
},
|
||||
OutputRules: OutputRules{Default: OutputToNothing},
|
||||
}
|
||||
|
@ -165,14 +193,23 @@ func (r *Runtime) Run() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
hadErrs := false
|
||||
for _, gen := range r.Generators {
|
||||
ctx := r.GenerationContext // make a shallow copy
|
||||
ctx.OutputRule = r.OutputRules.ForGenerator(gen)
|
||||
|
||||
// don't pass a typechecker to generators that don't provide a filter
|
||||
// to avoid accidents
|
||||
if _, needsChecking := (*gen).(NeedsTypeChecking); !needsChecking {
|
||||
ctx.Checker = nil
|
||||
}
|
||||
|
||||
if err := (*gen).Generate(&ctx); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
hadErrs = true
|
||||
}
|
||||
}
|
||||
|
||||
// skip TypeErrors -- they're probably just from partial typechecking in crd-gen
|
||||
return loader.PrintErrors(r.Roots, packages.TypeError)
|
||||
return loader.PrintErrors(r.Roots, packages.TypeError) || hadErrs
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package loader
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
|
@ -29,10 +28,15 @@ type PositionedError struct {
|
|||
error
|
||||
}
|
||||
|
||||
// Node is the intersection of go/ast.Node and go/types.Var.
|
||||
type Node interface {
|
||||
Pos() token.Pos // position of first character belonging to the node
|
||||
}
|
||||
|
||||
// ErrFromNode returns the given error, with additional information
|
||||
// attaching it to the given AST node. It will automatically map
|
||||
// over error lists.
|
||||
func ErrFromNode(err error, node ast.Node) error {
|
||||
func ErrFromNode(err error, node Node) error {
|
||||
if asList, isList := err.(ErrList); isList {
|
||||
resList := make(ErrList, len(asList))
|
||||
for i, baseErr := range asList {
|
||||
|
|
|
@ -185,37 +185,51 @@ func allReferencedPackages(pkg *Package, filterNodes NodeFilter) []*Package {
|
|||
// checking each package's types' externally-referenced types, and only
|
||||
// type-checking those packages.
|
||||
type TypeChecker struct {
|
||||
// NodeFilters are used to filter the set of references that are followed
|
||||
// when typechecking. If any of the filters returns true for a given node,
|
||||
// its package will be added to the set of packages to check.
|
||||
//
|
||||
// If no filters are specified, all references are followed (this may be slow).
|
||||
//
|
||||
// Modifying this after the first call to check may yield strange/invalid
|
||||
// results.
|
||||
NodeFilters []NodeFilter
|
||||
|
||||
checkedPackages map[*Package]struct{}
|
||||
filterNodes NodeFilter
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// Check type-checks the given package and all packages referenced
|
||||
// by types that pass through (have true returned by) filterNodes.
|
||||
func (c *TypeChecker) Check(root *Package, filterNodes NodeFilter) {
|
||||
// Check type-checks the given package and all packages referenced by types
|
||||
// that pass through (have true returned by) any of the NodeFilters.
|
||||
func (c *TypeChecker) Check(root *Package) {
|
||||
c.init()
|
||||
|
||||
if filterNodes == nil {
|
||||
filterNodes = c.filterNodes
|
||||
}
|
||||
|
||||
// use a sub-checker with the appropriate settings
|
||||
(&TypeChecker{
|
||||
filterNodes: filterNodes,
|
||||
NodeFilters: c.NodeFilters,
|
||||
checkedPackages: c.checkedPackages,
|
||||
}).check(root)
|
||||
}
|
||||
|
||||
func (c *TypeChecker) isNodeInteresting(node ast.Node) bool {
|
||||
// no filters --> everything is important
|
||||
if len(c.NodeFilters) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// otherwise, passing through any one filter means this node is important
|
||||
for _, filter := range c.NodeFilters {
|
||||
if filter(node) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *TypeChecker) init() {
|
||||
if c.checkedPackages == nil {
|
||||
c.checkedPackages = make(map[*Package]struct{})
|
||||
}
|
||||
if c.filterNodes == nil {
|
||||
// check every type by default
|
||||
c.filterNodes = func(_ ast.Node) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check recursively type-checks the given package, only loading packages that
|
||||
|
@ -232,7 +246,7 @@ func (c *TypeChecker) check(root *Package) {
|
|||
return
|
||||
}
|
||||
|
||||
refedPackages := allReferencedPackages(root, c.filterNodes)
|
||||
refedPackages := allReferencedPackages(root, c.isNodeInteresting)
|
||||
|
||||
// first, resolve imports for all leaf packages...
|
||||
var wg sync.WaitGroup
|
||||
|
|
|
@ -340,8 +340,13 @@ func guessType(scanner *sc.Scanner, raw string, allowSlice bool) *Argument {
|
|||
}
|
||||
}
|
||||
|
||||
// then, integers...
|
||||
if !probablyString {
|
||||
if nextTok := subScanner.Scan(); nextTok == sc.Int {
|
||||
nextTok := subScanner.Scan()
|
||||
if nextTok == '-' {
|
||||
nextTok = subScanner.Scan()
|
||||
}
|
||||
if nextTok == sc.Int {
|
||||
return &Argument{Type: IntType}
|
||||
}
|
||||
}
|
||||
|
@ -481,11 +486,21 @@ func (a *Argument) parse(scanner *sc.Scanner, raw string, out reflect.Value, inS
|
|||
for tok := scanner.Scan(); tok != sc.EOF; tok = scanner.Scan() {
|
||||
}
|
||||
case IntType:
|
||||
nextChar := scanner.Peek()
|
||||
isNegative := false
|
||||
if nextChar == '-' {
|
||||
isNegative = true
|
||||
scanner.Scan() // eat the '-'
|
||||
}
|
||||
if !expect(scanner, sc.Int, "integer") {
|
||||
return
|
||||
}
|
||||
// TODO(directxman12): respect the size when parsing
|
||||
val, err := strconv.Atoi(scanner.TokenText())
|
||||
text := scanner.TokenText()
|
||||
if isNegative {
|
||||
text = "-" + text
|
||||
}
|
||||
val, err := strconv.Atoi(text)
|
||||
if err != nil {
|
||||
scanner.Error(scanner, fmt.Sprintf("unable to parse integer: %v", err))
|
||||
return
|
||||
|
|
|
@ -32,6 +32,7 @@ import (
|
|||
crdgen "sigs.k8s.io/controller-tools/pkg/crd"
|
||||
crdmarkers "sigs.k8s.io/controller-tools/pkg/crd/markers"
|
||||
"sigs.k8s.io/controller-tools/pkg/genall"
|
||||
"sigs.k8s.io/controller-tools/pkg/loader"
|
||||
"sigs.k8s.io/controller-tools/pkg/markers"
|
||||
yamlop "sigs.k8s.io/controller-tools/pkg/schemapatcher/internal/yaml"
|
||||
)
|
||||
|
@ -87,6 +88,10 @@ type Generator struct {
|
|||
|
||||
var _ genall.Generator = &Generator{}
|
||||
|
||||
func (Generator) CheckFilter() loader.NodeFilter {
|
||||
return crdgen.Generator{}.CheckFilter()
|
||||
}
|
||||
|
||||
func (Generator) RegisterMarkers(into *markers.Registry) error {
|
||||
return crdmarkers.Register(into)
|
||||
}
|
||||
|
|
|
@ -19,26 +19,37 @@ limitations under the License.
|
|||
//
|
||||
// The markers take the form:
|
||||
//
|
||||
// +kubebuilder:webhook:failurePolicy=<string>,matchPolicy=<string>,groups=<[]string>,resources=<[]string>,verbs=<[]string>,versions=<[]string>,name=<string>,path=<string>,mutating=<bool>,sideEffects=<string>
|
||||
// +kubebuilder:webhook:webhookVersions=<[]string>,failurePolicy=<string>,matchPolicy=<string>,groups=<[]string>,resources=<[]string>,verbs=<[]string>,versions=<[]string>,name=<string>,path=<string>,mutating=<bool>,sideEffects=<string>,admissionReviewVersions=<[]string>
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
admissionreg "k8s.io/api/admissionregistration/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
admissionregv1 "k8s.io/api/admissionregistration/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
|
||||
"sigs.k8s.io/controller-tools/pkg/genall"
|
||||
"sigs.k8s.io/controller-tools/pkg/markers"
|
||||
)
|
||||
|
||||
// The default {Mutating,Validating}WebhookConfiguration version to generate.
|
||||
const (
|
||||
defaultWebhookVersion = "v1"
|
||||
)
|
||||
|
||||
var (
|
||||
// ConfigDefinition s a marker for defining Webhook manifests.
|
||||
// Call ToWebhook on the value to get a Kubernetes Webhook.
|
||||
ConfigDefinition = markers.Must(markers.MakeDefinition("kubebuilder:webhook", markers.DescribesPackage, Config{}))
|
||||
)
|
||||
|
||||
// supportedWebhookVersions returns currently supported API version of {Mutating,Validating}WebhookConfiguration.
|
||||
func supportedWebhookVersions() []string {
|
||||
return []string{defaultWebhookVersion, "v1beta1"}
|
||||
}
|
||||
|
||||
// +controllertools:marker:generateHelp:category=Webhook
|
||||
|
||||
// Config specifies how a webhook should be served.
|
||||
|
@ -91,79 +102,91 @@ type Config struct {
|
|||
// batch.tutorial.kubebuilder.io/v1,Kind=CronJob would be
|
||||
// /validate-batch-tutorial-kubebuilder-io-v1-cronjob
|
||||
Path string
|
||||
|
||||
// WebhookVersions specifies the target API versions of the {Mutating,Validating}WebhookConfiguration objects
|
||||
// itself to generate. 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"`
|
||||
}
|
||||
|
||||
// verbToAPIVariant converts a marker's verb to the proper value for the API.
|
||||
// Unrecognized verbs are passed through.
|
||||
func verbToAPIVariant(verbRaw string) admissionreg.OperationType {
|
||||
func verbToAPIVariant(verbRaw string) admissionregv1.OperationType {
|
||||
switch strings.ToLower(verbRaw) {
|
||||
case strings.ToLower(string(admissionreg.Create)):
|
||||
return admissionreg.Create
|
||||
case strings.ToLower(string(admissionreg.Update)):
|
||||
return admissionreg.Update
|
||||
case strings.ToLower(string(admissionreg.Delete)):
|
||||
return admissionreg.Delete
|
||||
case strings.ToLower(string(admissionreg.Connect)):
|
||||
return admissionreg.Connect
|
||||
case strings.ToLower(string(admissionreg.OperationAll)):
|
||||
return admissionreg.OperationAll
|
||||
case strings.ToLower(string(admissionregv1.Create)):
|
||||
return admissionregv1.Create
|
||||
case strings.ToLower(string(admissionregv1.Update)):
|
||||
return admissionregv1.Update
|
||||
case strings.ToLower(string(admissionregv1.Delete)):
|
||||
return admissionregv1.Delete
|
||||
case strings.ToLower(string(admissionregv1.Connect)):
|
||||
return admissionregv1.Connect
|
||||
case strings.ToLower(string(admissionregv1.OperationAll)):
|
||||
return admissionregv1.OperationAll
|
||||
default:
|
||||
return admissionreg.OperationType(verbRaw)
|
||||
return admissionregv1.OperationType(verbRaw)
|
||||
}
|
||||
}
|
||||
|
||||
// ToMutatingWebhook converts this rule to its Kubernetes API form.
|
||||
func (c Config) ToMutatingWebhook() (admissionreg.MutatingWebhook, error) {
|
||||
func (c Config) ToMutatingWebhook() (admissionregv1.MutatingWebhook, error) {
|
||||
if !c.Mutating {
|
||||
return admissionreg.MutatingWebhook{}, fmt.Errorf("%s is a validating webhook", c.Name)
|
||||
return admissionregv1.MutatingWebhook{}, fmt.Errorf("%s is a validating webhook", c.Name)
|
||||
}
|
||||
|
||||
matchPolicy, err := c.matchPolicy()
|
||||
if err != nil {
|
||||
return admissionreg.MutatingWebhook{}, err
|
||||
return admissionregv1.MutatingWebhook{}, err
|
||||
}
|
||||
|
||||
return admissionreg.MutatingWebhook{
|
||||
Name: c.Name,
|
||||
Rules: c.rules(),
|
||||
FailurePolicy: c.failurePolicy(),
|
||||
MatchPolicy: matchPolicy,
|
||||
ClientConfig: c.clientConfig(),
|
||||
SideEffects: c.sideEffects(),
|
||||
return admissionregv1.MutatingWebhook{
|
||||
Name: c.Name,
|
||||
Rules: c.rules(),
|
||||
FailurePolicy: c.failurePolicy(),
|
||||
MatchPolicy: matchPolicy,
|
||||
ClientConfig: c.clientConfig(),
|
||||
SideEffects: c.sideEffects(),
|
||||
AdmissionReviewVersions: c.AdmissionReviewVersions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ToValidatingWebhook converts this rule to its Kubernetes API form.
|
||||
func (c Config) ToValidatingWebhook() (admissionreg.ValidatingWebhook, error) {
|
||||
func (c Config) ToValidatingWebhook() (admissionregv1.ValidatingWebhook, error) {
|
||||
if c.Mutating {
|
||||
return admissionreg.ValidatingWebhook{}, fmt.Errorf("%s is a mutating webhook", c.Name)
|
||||
return admissionregv1.ValidatingWebhook{}, fmt.Errorf("%s is a mutating webhook", c.Name)
|
||||
}
|
||||
|
||||
matchPolicy, err := c.matchPolicy()
|
||||
if err != nil {
|
||||
return admissionreg.ValidatingWebhook{}, err
|
||||
return admissionregv1.ValidatingWebhook{}, err
|
||||
}
|
||||
|
||||
return admissionreg.ValidatingWebhook{
|
||||
Name: c.Name,
|
||||
Rules: c.rules(),
|
||||
FailurePolicy: c.failurePolicy(),
|
||||
MatchPolicy: matchPolicy,
|
||||
ClientConfig: c.clientConfig(),
|
||||
SideEffects: c.sideEffects(),
|
||||
return admissionregv1.ValidatingWebhook{
|
||||
Name: c.Name,
|
||||
Rules: c.rules(),
|
||||
FailurePolicy: c.failurePolicy(),
|
||||
MatchPolicy: matchPolicy,
|
||||
ClientConfig: c.clientConfig(),
|
||||
SideEffects: c.sideEffects(),
|
||||
AdmissionReviewVersions: c.AdmissionReviewVersions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// rules returns the configuration of what operations on what
|
||||
// resources/subresources a webhook should care about.
|
||||
func (c Config) rules() []admissionreg.RuleWithOperations {
|
||||
whConfig := admissionreg.RuleWithOperations{
|
||||
Rule: admissionreg.Rule{
|
||||
func (c Config) rules() []admissionregv1.RuleWithOperations {
|
||||
whConfig := admissionregv1.RuleWithOperations{
|
||||
Rule: admissionregv1.Rule{
|
||||
APIGroups: c.Groups,
|
||||
APIVersions: c.Versions,
|
||||
Resources: c.Resources,
|
||||
},
|
||||
Operations: make([]admissionreg.OperationType, len(c.Verbs)),
|
||||
Operations: make([]admissionregv1.OperationType, len(c.Verbs)),
|
||||
}
|
||||
|
||||
for i, verbRaw := range c.Verbs {
|
||||
|
@ -177,32 +200,32 @@ func (c Config) rules() []admissionreg.RuleWithOperations {
|
|||
}
|
||||
}
|
||||
|
||||
return []admissionreg.RuleWithOperations{whConfig}
|
||||
return []admissionregv1.RuleWithOperations{whConfig}
|
||||
}
|
||||
|
||||
// failurePolicy converts the string value to the proper value for the API.
|
||||
// Unrecognized values are passed through.
|
||||
func (c Config) failurePolicy() *admissionreg.FailurePolicyType {
|
||||
var failurePolicy admissionreg.FailurePolicyType
|
||||
func (c Config) failurePolicy() *admissionregv1.FailurePolicyType {
|
||||
var failurePolicy admissionregv1.FailurePolicyType
|
||||
switch strings.ToLower(c.FailurePolicy) {
|
||||
case strings.ToLower(string(admissionreg.Ignore)):
|
||||
failurePolicy = admissionreg.Ignore
|
||||
case strings.ToLower(string(admissionreg.Fail)):
|
||||
failurePolicy = admissionreg.Fail
|
||||
case strings.ToLower(string(admissionregv1.Ignore)):
|
||||
failurePolicy = admissionregv1.Ignore
|
||||
case strings.ToLower(string(admissionregv1.Fail)):
|
||||
failurePolicy = admissionregv1.Fail
|
||||
default:
|
||||
failurePolicy = admissionreg.FailurePolicyType(c.FailurePolicy)
|
||||
failurePolicy = admissionregv1.FailurePolicyType(c.FailurePolicy)
|
||||
}
|
||||
return &failurePolicy
|
||||
}
|
||||
|
||||
// matchPolicy converts the string value to the proper value for the API.
|
||||
func (c Config) matchPolicy() (*admissionreg.MatchPolicyType, error) {
|
||||
var matchPolicy admissionreg.MatchPolicyType
|
||||
func (c Config) matchPolicy() (*admissionregv1.MatchPolicyType, error) {
|
||||
var matchPolicy admissionregv1.MatchPolicyType
|
||||
switch strings.ToLower(c.MatchPolicy) {
|
||||
case strings.ToLower(string(admissionreg.Exact)):
|
||||
matchPolicy = admissionreg.Exact
|
||||
case strings.ToLower(string(admissionreg.Equivalent)):
|
||||
matchPolicy = admissionreg.Equivalent
|
||||
case strings.ToLower(string(admissionregv1.Exact)):
|
||||
matchPolicy = admissionregv1.Exact
|
||||
case strings.ToLower(string(admissionregv1.Equivalent)):
|
||||
matchPolicy = admissionregv1.Equivalent
|
||||
case "":
|
||||
return nil, nil
|
||||
default:
|
||||
|
@ -212,31 +235,27 @@ func (c Config) matchPolicy() (*admissionreg.MatchPolicyType, error) {
|
|||
}
|
||||
|
||||
// clientConfig returns the client config for a webhook.
|
||||
func (c Config) clientConfig() admissionreg.WebhookClientConfig {
|
||||
func (c Config) clientConfig() admissionregv1.WebhookClientConfig {
|
||||
path := c.Path
|
||||
return admissionreg.WebhookClientConfig{
|
||||
Service: &admissionreg.ServiceReference{
|
||||
return admissionregv1.WebhookClientConfig{
|
||||
Service: &admissionregv1.ServiceReference{
|
||||
Name: "webhook-service",
|
||||
Namespace: "system",
|
||||
Path: &path,
|
||||
},
|
||||
// OpenAPI marks the field as required before 1.13 because of a bug that got fixed in
|
||||
// https://github.com/kubernetes/api/commit/e7d9121e9ffd63cea0288b36a82bcc87b073bd1b
|
||||
// Put "\n" as an placeholder as a workaround til 1.13+ is almost everywhere.
|
||||
CABundle: []byte("\n"),
|
||||
}
|
||||
}
|
||||
|
||||
// sideEffects returns the sideEffects config for a webhook.
|
||||
func (c Config) sideEffects() *admissionreg.SideEffectClass {
|
||||
var sideEffects admissionreg.SideEffectClass
|
||||
func (c Config) sideEffects() *admissionregv1.SideEffectClass {
|
||||
var sideEffects admissionregv1.SideEffectClass
|
||||
switch strings.ToLower(c.SideEffects) {
|
||||
case strings.ToLower(string(admissionreg.SideEffectClassNone)):
|
||||
sideEffects = admissionreg.SideEffectClassNone
|
||||
case strings.ToLower(string(admissionreg.SideEffectClassNoneOnDryRun)):
|
||||
sideEffects = admissionreg.SideEffectClassNoneOnDryRun
|
||||
case strings.ToLower(string(admissionreg.SideEffectClassSome)):
|
||||
sideEffects = admissionreg.SideEffectClassSome
|
||||
case strings.ToLower(string(admissionregv1.SideEffectClassNone)):
|
||||
sideEffects = admissionregv1.SideEffectClassNone
|
||||
case strings.ToLower(string(admissionregv1.SideEffectClassNoneOnDryRun)):
|
||||
sideEffects = admissionregv1.SideEffectClassNoneOnDryRun
|
||||
case strings.ToLower(string(admissionregv1.SideEffectClassSome)):
|
||||
sideEffects = admissionregv1.SideEffectClassSome
|
||||
case "":
|
||||
return nil
|
||||
default:
|
||||
|
@ -245,6 +264,21 @@ func (c Config) sideEffects() *admissionreg.SideEffectClass {
|
|||
return &sideEffects
|
||||
}
|
||||
|
||||
// webhookVersions returns the target API versions of the {Mutating,Validating}WebhookConfiguration objects for a webhook.
|
||||
func (c Config) webhookVersions() ([]string, error) {
|
||||
// If WebhookVersions is not specified, we default it to `v1`.
|
||||
if len(c.WebhookVersions) == 0 {
|
||||
return []string{defaultWebhookVersion}, nil
|
||||
}
|
||||
supportedWebhookVersions := sets.NewString(supportedWebhookVersions()...)
|
||||
for _, version := range c.WebhookVersions {
|
||||
if !supportedWebhookVersions.Has(version) {
|
||||
return nil, fmt.Errorf("unsupported webhook version: %s", version)
|
||||
}
|
||||
}
|
||||
return sets.NewString(c.WebhookVersions...).UnsortedList(), nil
|
||||
}
|
||||
|
||||
// +controllertools:marker:generateHelp
|
||||
|
||||
// Generator generates (partial) {Mutating,Validating}WebhookConfiguration objects.
|
||||
|
@ -259,8 +293,9 @@ func (Generator) RegisterMarkers(into *markers.Registry) error {
|
|||
}
|
||||
|
||||
func (Generator) Generate(ctx *genall.GenerationContext) error {
|
||||
var mutatingCfgs []admissionreg.MutatingWebhook
|
||||
var validatingCfgs []admissionreg.ValidatingWebhook
|
||||
supportedWebhookVersions := supportedWebhookVersions()
|
||||
mutatingCfgs := make(map[string][]admissionregv1.MutatingWebhook, len(supportedWebhookVersions))
|
||||
validatingCfgs := make(map[string][]admissionregv1.ValidatingWebhook, len(supportedWebhookVersions))
|
||||
for _, root := range ctx.Roots {
|
||||
markerSet, err := markers.PackageMarkers(ctx.Collector, root)
|
||||
if err != nil {
|
||||
|
@ -269,47 +304,114 @@ func (Generator) Generate(ctx *genall.GenerationContext) error {
|
|||
|
||||
for _, cfg := range markerSet[ConfigDefinition.Name] {
|
||||
cfg := cfg.(Config)
|
||||
webhookVersions, err := cfg.webhookVersions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cfg.Mutating {
|
||||
w, _ := cfg.ToMutatingWebhook()
|
||||
mutatingCfgs = append(mutatingCfgs, w)
|
||||
w, err := cfg.ToMutatingWebhook()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, webhookVersion := range webhookVersions {
|
||||
mutatingCfgs[webhookVersion] = append(mutatingCfgs[webhookVersion], w)
|
||||
}
|
||||
} else {
|
||||
w, _ := cfg.ToValidatingWebhook()
|
||||
validatingCfgs = append(validatingCfgs, w)
|
||||
w, err := cfg.ToValidatingWebhook()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, webhookVersion := range webhookVersions {
|
||||
validatingCfgs[webhookVersion] = append(validatingCfgs[webhookVersion], w)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var objs []interface{}
|
||||
if len(mutatingCfgs) > 0 {
|
||||
objs = append(objs, &admissionreg.MutatingWebhookConfiguration{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "MutatingWebhookConfiguration",
|
||||
APIVersion: admissionreg.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "mutating-webhook-configuration",
|
||||
},
|
||||
Webhooks: mutatingCfgs,
|
||||
})
|
||||
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.
|
||||
objRaw := &admissionregv1.MutatingWebhookConfiguration{}
|
||||
objRaw.SetGroupVersionKind(schema.GroupVersionKind{
|
||||
Group: admissionregv1.SchemeGroupVersion.Group,
|
||||
Version: version,
|
||||
Kind: "MutatingWebhookConfiguration",
|
||||
})
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
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.
|
||||
objRaw := &admissionregv1.ValidatingWebhookConfiguration{}
|
||||
objRaw.SetGroupVersionKind(schema.GroupVersionKind{
|
||||
Group: admissionregv1.SchemeGroupVersion.Group,
|
||||
Version: version,
|
||||
Kind: "ValidatingWebhookConfiguration",
|
||||
})
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
versionedWebhooks[version] = append(versionedWebhooks[version], objRaw)
|
||||
}
|
||||
}
|
||||
|
||||
if len(validatingCfgs) > 0 {
|
||||
objs = append(objs, &admissionreg.ValidatingWebhookConfiguration{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ValidatingWebhookConfiguration",
|
||||
APIVersion: admissionreg.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "validating-webhook-configuration",
|
||||
},
|
||||
Webhooks: validatingCfgs,
|
||||
})
|
||||
|
||||
for k, v := range versionedWebhooks {
|
||||
var fileName string
|
||||
if k == defaultWebhookVersion {
|
||||
fileName = fmt.Sprintf("manifests.yaml")
|
||||
} else {
|
||||
fileName = fmt.Sprintf("manifests.%s.yaml", k)
|
||||
}
|
||||
if err := ctx.WriteYAML(fileName, v...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkSideEffectsForV1(sideEffects *admissionregv1.SideEffectClass) error {
|
||||
if sideEffects == nil {
|
||||
return fmt.Errorf("SideEffects is required for creating v1 {Mutating,Validating}WebhookConfiguration")
|
||||
}
|
||||
if *sideEffects == admissionregv1.SideEffectClassUnknown ||
|
||||
*sideEffects == admissionregv1.SideEffectClassSome {
|
||||
return fmt.Errorf("SideEffects should not be set to `Some` or `Unknown` for v1 {Mutating,Validating}WebhookConfiguration")
|
||||
}
|
||||
|
||||
if len(objs) > 0 {
|
||||
return ctx.WriteYAML("manifests.yaml", objs...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
8
vendor/sigs.k8s.io/controller-tools/pkg/webhook/zz_generated.markerhelp.go
сгенерированный
поставляемый
8
vendor/sigs.k8s.io/controller-tools/pkg/webhook/zz_generated.markerhelp.go
сгенерированный
поставляемый
|
@ -72,6 +72,14 @@ func (Config) Help() *markers.DefinitionHelp {
|
|||
Summary: "specifies that path that the API server should connect to this webhook on. Must be prefixed with a '/validate-' or '/mutate-' depending on the type, and followed by $GROUP-$VERSION-$KIND where all values are lower-cased and the periods in the group are substituted for hyphens. For example, a validating webhook path for type batch.tutorial.kubebuilder.io/v1,Kind=CronJob would be /validate-batch-tutorial-kubebuilder-io-v1-cronjob",
|
||||
Details: "",
|
||||
},
|
||||
"WebhookVersions": markers.DetailedHelp{
|
||||
Summary: "specifies the target API versions of the {Mutating,Validating}WebhookConfiguration objects itself to generate. Defaults to v1.",
|
||||
Details: "",
|
||||
},
|
||||
"AdmissionReviewVersions": markers.DetailedHelp{
|
||||
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.",
|
||||
Details: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче