1
0
mirror of https://github.com/kubernetes-sigs/descheduler.git synced 2026-01-28 06:29:29 +01:00

[v0.34.0] bump to kubernetes 1.34 deps

Signed-off-by: Amir Alavi <amiralavi7@gmail.com>
This commit is contained in:
Amir Alavi
2025-09-17 16:55:29 -04:00
parent e9188852ef
commit 1db6b615d1
1266 changed files with 100906 additions and 40660 deletions

View File

@@ -1,3 +1,8 @@
> ⚠️ **This is an automatically published [staged repository](https://git.k8s.io/kubernetes/staging#external-repository-staging-area) for Kubernetes**.
> Contributions, including issues and pull requests, should be made to the main Kubernetes repository: [https://github.com/kubernetes/kubernetes](https://github.com/kubernetes/kubernetes).
> This repository is read-only for importing, and not used for direct contributions.
> See [CONTRIBUTING.md](./CONTRIBUTING.md) for more details.
# code-generator
Golang code-generators used to implement [Kubernetes-style API types](https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md).

View File

@@ -115,6 +115,10 @@ func (g *applyConfigurationGenerator) GenerateType(c *generator.Context, t *type
sw.Do(constructor, typeParams)
}
}
if typeParams.Tags.GenerateClient || hasTypeMetaField(t) {
g.generateIsApplyConfiguration(typeParams.ApplyConfig.ApplyConfiguration, sw)
}
g.generateWithFuncs(t, typeParams, sw, nil, &[]string{})
g.generateGetters(t, typeParams, sw, nil)
return sw.Error()
@@ -145,7 +149,8 @@ func blocklisted(t *types.Type, member types.Member) bool {
func needsGetter(t *types.Type, member types.Member) bool {
// Needed when applying an ApplyConfiguration
return objectMeta.Name == t.Name && member.Name == "Name"
return (objectMeta.Name == t.Name && (member.Name == "Name" || member.Name == "Namespace")) ||
(typeMeta.Name == t.Name && (member.Name == "Kind" || member.Name == "APIVersion"))
}
func (g *applyConfigurationGenerator) generateGetters(t *types.Type, typeParams TypeParams, sw *generator.SnippetWriter, embed *memberParams) {
@@ -270,6 +275,10 @@ func (g *applyConfigurationGenerator) generateStruct(sw *generator.SnippetWriter
sw.Do("}\n", typeParams)
}
func (g *applyConfigurationGenerator) generateIsApplyConfiguration(t *types.Type, sw *generator.SnippetWriter) {
sw.Do("func (b $.|public$) IsApplyConfiguration() {}\n", t)
}
func deref(t *types.Type) *types.Type {
for t.Kind == types.Pointer {
t = t.Elem
@@ -288,9 +297,9 @@ func (g *applyConfigurationGenerator) generateMemberWith(sw *generator.SnippetWr
sw.Do("func (b *$.ApplyConfig.ApplyConfiguration|public$) With$.Member.Name$(value $.MemberType|raw$) *$.ApplyConfig.ApplyConfiguration|public$ {\n", memberParams)
g.ensureEmbedExistsIfApplicable(sw, memberParams)
if g.refGraph.isApplyConfig(memberParams.Member.Type) || isNillable(memberParams.Member.Type) {
sw.Do("b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$ = value\n", memberParams)
sw.Do("b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$ = value\n", memberParams)
} else {
sw.Do("b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$ = &value\n", memberParams)
sw.Do("b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$ = &value\n", memberParams)
}
sw.Do(" return b\n", memberParams)
sw.Do("}\n", memberParams)
@@ -304,7 +313,7 @@ func (g *applyConfigurationGenerator) generateMemberGetter(sw *generator.Snippet
sw.Do("func (b *$.ApplyConfig.ApplyConfiguration|public$) Get$.Member.Name$() *$.MemberType|raw$ {\n", memberParams)
}
g.ensureEmbedExistsIfApplicable(sw, memberParams)
sw.Do(" return b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$\n", memberParams)
sw.Do(" return b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$\n", memberParams)
sw.Do("}\n", memberParams)
}
@@ -331,15 +340,15 @@ func (g *applyConfigurationGenerator) generateMemberWithForSlice(sw *generator.S
sw.Do("}\n", memberParams)
if memberIsPointerToSlice {
sw.Do("*b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$ = append(*b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$, *values[i])\n", memberParams)
sw.Do("*b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$ = append(*b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$, *values[i])\n", memberParams)
} else {
sw.Do("b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$ = append(b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$, *values[i])\n", memberParams)
sw.Do("b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$ = append(b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$, *values[i])\n", memberParams)
}
} else {
if memberIsPointerToSlice {
sw.Do("*b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$ = append(*b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$, values[i])\n", memberParams)
sw.Do("*b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$ = append(*b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$, values[i])\n", memberParams)
} else {
sw.Do("b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$ = append(b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$, values[i])\n", memberParams)
sw.Do("b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$ = append(b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$, values[i])\n", memberParams)
}
}
sw.Do(" }\n", memberParams)
@@ -354,11 +363,11 @@ func (g *applyConfigurationGenerator) generateMemberWithForMap(sw *generator.Sni
sw.Do("// overwriting an existing map entries in $.Member.Name$ field with the same key.\n", memberParams)
sw.Do("func (b *$.ApplyConfig.ApplyConfiguration|public$) With$.Member.Name$(entries $.MemberType|raw$) *$.ApplyConfig.ApplyConfiguration|public$ {\n", memberParams)
g.ensureEmbedExistsIfApplicable(sw, memberParams)
sw.Do(" if b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$ == nil && len(entries) > 0 {\n", memberParams)
sw.Do(" b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$ = make($.MemberType|raw$, len(entries))\n", memberParams)
sw.Do(" if b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$ == nil && len(entries) > 0 {\n", memberParams)
sw.Do(" b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$ = make($.MemberType|raw$, len(entries))\n", memberParams)
sw.Do(" }\n", memberParams)
sw.Do(" for k, v := range entries {\n", memberParams)
sw.Do(" b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$[k] = v\n", memberParams)
sw.Do(" b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$[k] = v\n", memberParams)
sw.Do(" }\n", memberParams)
sw.Do(" return b\n", memberParams)
sw.Do("}\n", memberParams)

View File

@@ -19,11 +19,11 @@ package generators
import (
"io"
yaml "go.yaml.in/yaml/v2"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
"k8s.io/gengo/v2/types"
"k8s.io/kube-openapi/pkg/schemaconv"
yaml "sigs.k8s.io/yaml/goyaml.v2"
)
// utilGenerator generates the ForKind() utility function.

View File

@@ -26,6 +26,7 @@ import (
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
"k8s.io/gengo/v2/types"
"k8s.io/kube-openapi/pkg/util"
utilproto "k8s.io/kube-openapi/pkg/util/proto"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@@ -57,12 +58,15 @@ func newTypeModels(openAPISchemaFilePath string, pkgTypes map[string]*types.Pack
gvkToOpenAPIType := map[clientgentypes.GroupVersionKind]string{}
rootDefs := map[string]spec.Schema{}
for _, p := range pkgTypes {
gv := groupVersion(p)
gv, err := groupVersion(p)
if err != nil {
return nil, fmt.Errorf("failed to parse comments of package %s: %w", p.Name, err)
}
for _, t := range p.Types {
tags := genclientTags(t)
hasApply := tags.HasVerb("apply") || tags.HasVerb("applyStatus")
if tags.GenerateClient && hasApply {
openAPIType := friendlyName(typeName(t))
openAPIType := util.ToRESTFriendlyName(typeName(t))
gvk := gv.WithKind(clientgentypes.Kind(t.Name.Name))
rootDefs[openAPIType] = openAPISchema.Definitions[openAPIType]
gvkToOpenAPIType[gvk] = openAPIType

View File

@@ -32,6 +32,7 @@ import (
"k8s.io/code-generator/cmd/applyconfiguration-gen/args"
"k8s.io/code-generator/cmd/client-gen/generators/util"
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
genutil "k8s.io/code-generator/pkg/util"
)
const (
@@ -75,7 +76,10 @@ func GetTargets(context *generator.Context, args *args.Args) []generator.Target
var targetList []generator.Target
for pkg, p := range pkgTypes {
gv := groupVersion(p)
gv, err := groupVersion(p)
if err != nil {
klog.Fatalf("Failed to parse comments of package %s: %v", p.Name, err)
}
var toGenerate []applyConfig
for _, t := range p.Types {
@@ -128,7 +132,10 @@ func GetTargets(context *generator.Context, args *args.Args) []generator.Target
Package: path.Clean(p.Path),
})
groupGoNames[groupPackageName] = goName(gv, p)
groupGoNames[groupPackageName], err = goName(gv, p)
if err != nil {
klog.Fatalf("Failed to parse comments of group package %s: %v", groupPackageName, err)
}
applyConfigsForGroupVersion[gv] = toGenerate
groupVersions[groupPackageName] = groupVersionsEntry
}
@@ -145,19 +152,6 @@ func GetTargets(context *generator.Context, args *args.Args) []generator.Target
return targetList
}
func friendlyName(name string) string {
nameParts := strings.Split(name, "/")
// Reverse first part. e.g., io.k8s... instead of k8s.io...
if len(nameParts) > 0 && strings.Contains(nameParts[0], ".") {
parts := strings.Split(nameParts[0], ".")
for i, j := 0, len(parts)-1; i < j; i, j = i+1, j-1 {
parts[i], parts[j] = parts[j], parts[i]
}
nameParts[0] = strings.Join(parts, ".")
}
return strings.Join(nameParts, ".")
}
func typeName(t *types.Type) string {
typePackage := t.Name.Package
return fmt.Sprintf("%s.%s", typePackage, t.Name.Name)
@@ -244,12 +238,18 @@ func targetForInternal(outputDirBase, outputPkgBase string, boilerplate []byte,
}
}
func goName(gv clientgentypes.GroupVersion, p *types.Package) string {
func goName(gv clientgentypes.GroupVersion, p *types.Package) (string, error) {
goName := namer.IC(strings.Split(gv.Group.NonEmpty(), ".")[0])
if override := gengo.ExtractCommentTags("+", p.Comments)["groupGoName"]; override != nil {
goName = namer.IC(override[0])
override, err := genutil.ExtractCommentTagsWithoutArguments("+", []string{"groupGoName"}, p.Comments)
if err != nil {
return goName, err
}
return goName
if values, ok := override["groupGoName"]; ok {
goName = namer.IC(values[0])
}
return goName, nil
}
func packageTypesForInputs(context *generator.Context, outPkgBase string) map[string]*types.Package {
@@ -272,7 +272,7 @@ func packageTypesForInputs(context *generator.Context, outPkgBase string) map[st
return pkgTypes
}
func groupVersion(p *types.Package) (gv clientgentypes.GroupVersion) {
func groupVersion(p *types.Package) (gv clientgentypes.GroupVersion, err error) {
parts := strings.Split(p.Path, "/")
gv.Group = clientgentypes.Group(parts[len(parts)-2])
gv.Version = clientgentypes.Version(parts[len(parts)-1])
@@ -280,10 +280,16 @@ func groupVersion(p *types.Package) (gv clientgentypes.GroupVersion) {
// If there's a comment of the form "// +groupName=somegroup" or
// "// +groupName=somegroup.foo.bar.io", use the first field (somegroup) as the name of the
// group when generating.
if override := gengo.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil {
gv.Group = clientgentypes.Group(override[0])
override, err := genutil.ExtractCommentTagsWithoutArguments("+", []string{"groupName"}, p.Comments)
if err != nil {
return gv, err
}
return gv
if values, ok := override["groupName"]; ok {
gv.Group = clientgentypes.Group(values[0])
}
return gv, nil
}
// isInternalPackage returns true if the package is an internal package

View File

@@ -19,18 +19,19 @@ package generators
import "k8s.io/gengo/v2/types"
var (
fmtSprintf = types.Ref("fmt", "Sprintf")
syncOnce = types.Ref("sync", "Once")
applyConfiguration = types.Ref("k8s.io/apimachinery/pkg/runtime", "ApplyConfiguration")
groupVersionKind = types.Ref("k8s.io/apimachinery/pkg/runtime/schema", "GroupVersionKind")
typeMeta = types.Ref("k8s.io/apimachinery/pkg/apis/meta/v1", "TypeMeta")
objectMeta = types.Ref("k8s.io/apimachinery/pkg/apis/meta/v1", "ObjectMeta")
rawExtension = types.Ref("k8s.io/apimachinery/pkg/runtime", "RawExtension")
unknown = types.Ref("k8s.io/apimachinery/pkg/runtime", "Unknown")
extractInto = types.Ref("k8s.io/apimachinery/pkg/util/managedfields", "ExtractInto")
runtimeScheme = types.Ref("k8s.io/apimachinery/pkg/runtime", "Scheme")
smdNewParser = types.Ref("sigs.k8s.io/structured-merge-diff/v4/typed", "NewParser")
smdParser = types.Ref("sigs.k8s.io/structured-merge-diff/v4/typed", "Parser")
testingTypeConverter = types.Ref("k8s.io/client-go/testing", "TypeConverter")
yamlObject = types.Ref("sigs.k8s.io/structured-merge-diff/v4/typed", "YAMLObject")
fmtSprintf = types.Ref("fmt", "Sprintf")
syncOnce = types.Ref("sync", "Once")
applyConfiguration = types.Ref("k8s.io/apimachinery/pkg/runtime", "ApplyConfiguration")
groupVersionKind = types.Ref("k8s.io/apimachinery/pkg/runtime/schema", "GroupVersionKind")
typeMeta = types.Ref("k8s.io/apimachinery/pkg/apis/meta/v1", "TypeMeta")
objectMeta = types.Ref("k8s.io/apimachinery/pkg/apis/meta/v1", "ObjectMeta")
rawExtension = types.Ref("k8s.io/apimachinery/pkg/runtime", "RawExtension")
unknown = types.Ref("k8s.io/apimachinery/pkg/runtime", "Unknown")
extractInto = types.Ref("k8s.io/apimachinery/pkg/util/managedfields", "ExtractInto")
typeConverter = types.Ref("k8s.io/apimachinery/pkg/util/managedfields", "TypeConverter")
newSchemeTypeConverter = types.Ref("k8s.io/apimachinery/pkg/util/managedfields", "NewSchemeTypeConverter")
runtimeScheme = types.Ref("k8s.io/apimachinery/pkg/runtime", "Scheme")
smdNewParser = types.Ref("sigs.k8s.io/structured-merge-diff/v6/typed", "NewParser")
smdParser = types.Ref("sigs.k8s.io/structured-merge-diff/v6/typed", "Parser")
yamlObject = types.Ref("sigs.k8s.io/structured-merge-diff/v6/typed", "YAMLObject")
)

View File

@@ -142,17 +142,18 @@ func (g *utilGenerator) GenerateType(c *generator.Context, _ *types.Type, w io.W
"runtimeScheme": runtimeScheme,
"schemeGVs": schemeGVs,
"schemaGroupVersionKind": groupVersionKind,
"testingTypeConverter": testingTypeConverter,
"typeConverter": typeConverter,
"newSchemeTypeConverter": newSchemeTypeConverter,
}
sw.Do(forKindFunc, m)
sw.Do(typeConverter, m)
sw.Do(newTypeConverterFunc, m)
return sw.Error()
}
var typeConverter = `
func NewTypeConverter(scheme *{{.runtimeScheme|raw}}) *{{.testingTypeConverter|raw}} {
return &{{.testingTypeConverter|raw}}{Scheme: scheme, TypeResolver: {{.internalParser|raw}}()}
var newTypeConverterFunc = `
func NewTypeConverter(scheme *{{.runtimeScheme|raw}}) {{.typeConverter|raw}} {
return {{.newSchemeTypeConverter|raw}}(scheme, {{.internalParser|raw}}())
}
`

View File

@@ -29,6 +29,7 @@ import (
"k8s.io/code-generator/cmd/client-gen/generators/util"
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
codegennamer "k8s.io/code-generator/pkg/namer"
genutil "k8s.io/code-generator/pkg/util"
"k8s.io/gengo/v2"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
@@ -275,14 +276,18 @@ NextGroup:
// first field (somegroup) as the name of the group in Go code, e.g. as the func name in a clientset.
//
// If the first field of the groupName is not unique within the clientset, use "// +groupName=unique
func applyGroupOverrides(universe types.Universe, args *args.Args) {
func applyGroupOverrides(universe types.Universe, args *args.Args) error {
// Create a map from "old GV" to "new GV" so we know what changes we need to make.
changes := make(map[clientgentypes.GroupVersion]clientgentypes.GroupVersion)
for gv, inputDir := range args.GroupVersionPackages() {
p := universe.Package(inputDir)
if override := gengo.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil {
override, err := genutil.ExtractCommentTagsWithoutArguments("+", []string{"groupName"}, p.Comments)
if err != nil {
return fmt.Errorf("cannot extract groupName tags: %w", err)
}
if override["groupName"] != nil {
newGV := clientgentypes.GroupVersion{
Group: clientgentypes.Group(override[0]),
Group: clientgentypes.Group(override["groupName"][0]),
Version: gv.Version,
}
changes[gv] = newGV
@@ -310,6 +315,7 @@ func applyGroupOverrides(universe types.Universe, args *args.Args) {
}
}
args.Groups = newGroups
return nil
}
// Because we try to assemble inputs from an input-base and a set of
@@ -353,7 +359,9 @@ func GetTargets(context *generator.Context, args *args.Args) []generator.Target
if err := sanitizePackagePaths(context, args); err != nil {
klog.Fatalf("cannot sanitize inputs: %v", err)
}
applyGroupOverrides(context.Universe, args)
if err := applyGroupOverrides(context.Universe, args); err != nil {
klog.Fatalf("cannot apply group overrides: %v", err)
}
gvToTypes := map[clientgentypes.GroupVersion][]*types.Type{}
groupGoNames := make(map[clientgentypes.GroupVersion]string)
@@ -363,8 +371,12 @@ func GetTargets(context *generator.Context, args *args.Args) []generator.Target
// If there's a comment of the form "// +groupGoName=SomeUniqueShortName", use that as
// the Go group identifier in CamelCase. It defaults
groupGoNames[gv] = namer.IC(strings.Split(gv.Group.NonEmpty(), ".")[0])
if override := gengo.ExtractCommentTags("+", p.Comments)["groupGoName"]; override != nil {
groupGoNames[gv] = namer.IC(override[0])
override, err := genutil.ExtractCommentTagsWithoutArguments("+", []string{"groupGoName"}, p.Comments)
if err != nil {
klog.Fatalf("cannot extract groupGoName tags: %v", err)
}
if override["groupGoName"] != nil {
groupGoNames[gv] = namer.IC(override["groupGoName"][0])
}
for n, t := range p.Types {

View File

@@ -141,8 +141,8 @@ func NewClientset(objects ...runtime.Object) *Clientset {
cs.AddReactor("*", "*", testing.ObjectReaction(o))
cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
var opts metav1.ListOptions
if watchActcion, ok := action.(testing.WatchActionImpl); ok {
opts = watchActcion.ListOptions
if watchAction, ok := action.(testing.WatchActionImpl); ok {
opts = watchAction.ListOptions
}
gvr := action.GetResource()
ns := action.GetNamespace()

View File

@@ -20,7 +20,7 @@ import (
"io"
"path"
"k8s.io/gengo/v2"
genutil "k8s.io/code-generator/pkg/util"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
"k8s.io/gengo/v2/types"
@@ -73,8 +73,12 @@ func (g *genGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer
// allow user to define a group name that's different from the one parsed from the directory.
p := c.Universe.Package(g.inputPackage)
groupName := g.group
if override := gengo.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil {
groupName = override[0]
override, err := genutil.ExtractCommentTagsWithoutArguments("+", []string{"groupName"}, p.Comments)
if err != nil {
return err
}
if values, ok := override["groupName"]; ok {
groupName = values[0]
}
apiPath := `"` + g.apiPath + `"`

View File

@@ -151,47 +151,43 @@ func (g *genClientForType) GenerateType(c *generator.Context, t *types.Type, w i
extendedMethods = append(extendedMethods, extendedMethod)
}
m := map[string]interface{}{
"type": t,
"inputType": t,
"resultType": t,
"package": pkg,
"Package": namer.IC(pkg),
"namespaced": !tags.NonNamespaced,
"Group": namer.IC(g.group),
"subresource": false,
"subresourcePath": "",
"GroupGoName": g.groupGoName,
"prefersProtobuf": g.prefersProtobuf,
"Version": namer.IC(g.version),
"CreateOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "CreateOptions"}),
"DeleteOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "DeleteOptions"}),
"GetOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "GetOptions"}),
"ListOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "ListOptions"}),
"PatchOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "PatchOptions"}),
"ApplyOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "ApplyOptions"}),
"UpdateOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "UpdateOptions"}),
"PatchType": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/types", Name: "PatchType"}),
"watchInterface": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/watch", Name: "Interface"}),
"RESTClientInterface": c.Universe.Type(types.Name{Package: "k8s.io/client-go/rest", Name: "Interface"}),
"schemeParameterCodec": c.Universe.Variable(types.Name{Package: path.Join(g.clientsetPackage, "scheme"), Name: "ParameterCodec"}),
"fmtErrorf": c.Universe.Function(types.Name{Package: "fmt", Name: "Errorf"}),
"klogWarningf": c.Universe.Function(types.Name{Package: "k8s.io/klog/v2", Name: "Warningf"}),
"context": c.Universe.Type(types.Name{Package: "context", Name: "Context"}),
"timeDuration": c.Universe.Type(types.Name{Package: "time", Name: "Duration"}),
"timeSecond": c.Universe.Type(types.Name{Package: "time", Name: "Second"}),
"resourceVersionMatchNotOlderThan": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "ResourceVersionMatchNotOlderThan"}),
"CheckListFromCacheDataConsistencyIfRequested": c.Universe.Function(types.Name{Package: "k8s.io/client-go/util/consistencydetector", Name: "CheckListFromCacheDataConsistencyIfRequested"}),
"CheckWatchListFromCacheDataConsistencyIfRequested": c.Universe.Function(types.Name{Package: "k8s.io/client-go/util/consistencydetector", Name: "CheckWatchListFromCacheDataConsistencyIfRequested"}),
"PrepareWatchListOptionsFromListOptions": c.Universe.Function(types.Name{Package: "k8s.io/client-go/util/watchlist", Name: "PrepareWatchListOptionsFromListOptions"}),
"applyNewRequest": c.Universe.Function(types.Name{Package: "k8s.io/client-go/util/apply", Name: "NewRequest"}),
"Client": c.Universe.Type(types.Name{Package: "k8s.io/client-go/gentype", Name: "Client"}),
"ClientWithList": c.Universe.Type(types.Name{Package: "k8s.io/client-go/gentype", Name: "ClientWithList"}),
"ClientWithApply": c.Universe.Type(types.Name{Package: "k8s.io/client-go/gentype", Name: "ClientWithApply"}),
"ClientWithListAndApply": c.Universe.Type(types.Name{Package: "k8s.io/client-go/gentype", Name: "ClientWithListAndApply"}),
"NewClient": c.Universe.Function(types.Name{Package: "k8s.io/client-go/gentype", Name: "NewClient"}),
"NewClientWithApply": c.Universe.Function(types.Name{Package: "k8s.io/client-go/gentype", Name: "NewClientWithApply"}),
"NewClientWithList": c.Universe.Function(types.Name{Package: "k8s.io/client-go/gentype", Name: "NewClientWithList"}),
"NewClientWithListAndApply": c.Universe.Function(types.Name{Package: "k8s.io/client-go/gentype", Name: "NewClientWithListAndApply"}),
"type": t,
"inputType": t,
"resultType": t,
"package": pkg,
"Package": namer.IC(pkg),
"namespaced": !tags.NonNamespaced,
"Group": namer.IC(g.group),
"subresource": false,
"subresourcePath": "",
"GroupGoName": g.groupGoName,
"prefersProtobuf": g.prefersProtobuf,
"Version": namer.IC(g.version),
"CreateOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "CreateOptions"}),
"DeleteOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "DeleteOptions"}),
"GetOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "GetOptions"}),
"ListOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "ListOptions"}),
"PatchOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "PatchOptions"}),
"ApplyOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "ApplyOptions"}),
"UpdateOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "UpdateOptions"}),
"PatchType": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/types", Name: "PatchType"}),
"watchInterface": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/watch", Name: "Interface"}),
"RESTClientInterface": c.Universe.Type(types.Name{Package: "k8s.io/client-go/rest", Name: "Interface"}),
"schemeParameterCodec": c.Universe.Variable(types.Name{Package: path.Join(g.clientsetPackage, "scheme"), Name: "ParameterCodec"}),
"fmtErrorf": c.Universe.Function(types.Name{Package: "fmt", Name: "Errorf"}),
"klogWarningf": c.Universe.Function(types.Name{Package: "k8s.io/klog/v2", Name: "Warningf"}),
"context": c.Universe.Type(types.Name{Package: "context", Name: "Context"}),
"timeDuration": c.Universe.Type(types.Name{Package: "time", Name: "Duration"}),
"timeSecond": c.Universe.Type(types.Name{Package: "time", Name: "Second"}),
"applyNewRequest": c.Universe.Function(types.Name{Package: "k8s.io/client-go/util/apply", Name: "NewRequest"}),
"Client": c.Universe.Type(types.Name{Package: "k8s.io/client-go/gentype", Name: "Client"}),
"ClientWithList": c.Universe.Type(types.Name{Package: "k8s.io/client-go/gentype", Name: "ClientWithList"}),
"ClientWithApply": c.Universe.Type(types.Name{Package: "k8s.io/client-go/gentype", Name: "ClientWithApply"}),
"ClientWithListAndApply": c.Universe.Type(types.Name{Package: "k8s.io/client-go/gentype", Name: "ClientWithListAndApply"}),
"NewClient": c.Universe.Function(types.Name{Package: "k8s.io/client-go/gentype", Name: "NewClient"}),
"NewClientWithApply": c.Universe.Function(types.Name{Package: "k8s.io/client-go/gentype", Name: "NewClientWithApply"}),
"NewClientWithList": c.Universe.Function(types.Name{Package: "k8s.io/client-go/gentype", Name: "NewClientWithList"}),
"NewClientWithListAndApply": c.Universe.Function(types.Name{Package: "k8s.io/client-go/gentype", Name: "NewClientWithListAndApply"}),
}
if generateApply {
@@ -297,8 +293,6 @@ func (g *genClientForType) GenerateType(c *generator.Context, t *types.Type, w i
sw.Do(listSubresourceTemplate, m)
} else {
sw.Do(listTemplate, m)
sw.Do(privateListTemplate, m)
sw.Do(watchListTemplate, m)
}
}
@@ -602,28 +596,7 @@ var newStruct = []string{
var listTemplate = `
// $.verb$ takes label and field selectors, and returns the list of $.resultType|publicPlural$ that match those selectors.
func (c *$.type|privatePlural$) $.verb$(ctx $.context|raw$, opts $.ListOptions|raw$) (*$.resultType|raw$List, error) {
if watchListOptions, hasWatchListOptionsPrepared, watchListOptionsErr := $.PrepareWatchListOptionsFromListOptions|raw$(opts); watchListOptionsErr != nil {
$.klogWarningf|raw$("Failed preparing watchlist options for $.type|resource$, falling back to the standard LIST semantics, err = %v", watchListOptionsErr )
} else if hasWatchListOptionsPrepared {
result, err := c.watchList(ctx, watchListOptions)
if err == nil {
$.CheckWatchListFromCacheDataConsistencyIfRequested|raw$(ctx, "watchlist request for $.type|resource$", c.list, opts, result)
return result, nil
}
$.klogWarningf|raw$("The watchlist request for $.type|resource$ ended with an error, falling back to the standard LIST semantics, err = %v", err)
}
result, err := c.list(ctx, opts)
if err == nil {
$.CheckListFromCacheDataConsistencyIfRequested|raw$(ctx, "list request for $.type|resource$", c.list, opts, result)
}
return result, err
}
`
var privateListTemplate = `
// list takes label and field selectors, and returns the list of $.resultType|publicPlural$ that match those selectors.
func (c *$.type|privatePlural$) list(ctx $.context|raw$, opts $.ListOptions|raw$) (result *$.resultType|raw$List, err error) {
func (c *$.type|privatePlural$) $.verb$(ctx $.context|raw$, opts $.ListOptions|raw$) (result *$.resultType|raw$List, err error) {
var timeout $.timeDuration|raw$
if opts.TimeoutSeconds != nil{
timeout = $.timeDuration|raw$(*opts.TimeoutSeconds) * $.timeSecond|raw$
@@ -795,27 +768,6 @@ func (c *$.type|privatePlural$) $.verb$(ctx $.context|raw$, opts $.ListOptions|r
Watch(ctx)
}
`
var watchListTemplate = `
// watchList establishes a watch stream with the server and returns the list of $.resultType|publicPlural$
func (c *$.type|privatePlural$) watchList(ctx $.context|raw$, opts $.ListOptions|raw$) (result *$.resultType|raw$List, err error) {
var timeout $.timeDuration|raw$
if opts.TimeoutSeconds != nil{
timeout = $.timeDuration|raw$(*opts.TimeoutSeconds) * $.timeSecond|raw$
}
result = &$.resultType|raw$List{}
err = c.GetClient().Get().
$if .prefersProtobuf$UseProtobufAsDefault().$end$
$if .namespaced$Namespace(c.GetNamespace()).$end$
Resource("$.type|resource$").
VersionedParams(&opts, $.schemeParameterCodec|raw$).
Timeout(timeout).
WatchList(ctx).
Into(result)
return
}
`
var patchTemplate = `
// $.verb$ applies the patch and returns the patched $.resultType|private$.
func (c *$.type|privatePlural$) $.verb$(ctx $.context|raw$, name string, pt $.PatchType|raw$, data []byte, opts $.PatchOptions|raw$, subresources ...string) (result *$.resultType|raw$, err error) {

View File

@@ -48,26 +48,48 @@ const (
externalTypesTagName = "k8s:conversion-gen-external-types"
)
func extractTag(comments []string) []string {
return gengo.ExtractCommentTags("+", comments)[tagName]
func extractTagValues(tagName string, comments []string) ([]string, error) {
tags, err := gengo.ExtractFunctionStyleCommentTags("+", []string{tagName}, comments)
if err != nil {
return nil, err
}
tagList, exists := tags[tagName]
if !exists {
return nil, nil
}
values := make([]string, len(tagList))
for i, v := range tagList {
values[i] = v.Value
}
return values, nil
}
func extractExplicitFromTag(comments []string) []string {
return gengo.ExtractCommentTags("+", comments)[explicitFromTagName]
func extractTag(comments []string) ([]string, error) {
return extractTagValues(tagName, comments)
}
func extractExternalTypesTag(comments []string) []string {
return gengo.ExtractCommentTags("+", comments)[externalTypesTagName]
func extractExplicitFromTag(comments []string) ([]string, error) {
return extractTagValues(explicitFromTagName, comments)
}
func isCopyOnly(comments []string) bool {
values := gengo.ExtractCommentTags("+", comments)["k8s:conversion-fn"]
return len(values) == 1 && values[0] == "copy-only"
func extractExternalTypesTag(comments []string) ([]string, error) {
return extractTagValues(externalTypesTagName, comments)
}
func isDrop(comments []string) bool {
values := gengo.ExtractCommentTags("+", comments)["k8s:conversion-fn"]
return len(values) == 1 && values[0] == "drop"
func isCopyOnly(comments []string) (bool, error) {
values, err := extractTagValues("k8s:conversion-fn", comments)
if err != nil {
return false, err
}
return len(values) == 1 && values[0] == "copy-only", nil
}
func isDrop(comments []string) (bool, error) {
values, err := extractTagValues("k8s:conversion-fn", comments)
if err != nil {
return false, err
}
return len(values) == 1 && values[0] == "drop", nil
}
// TODO: This is created only to reduce number of changes in a single PR.
@@ -229,11 +251,15 @@ func GetTargets(context *generator.Context, args *args.Args) []generator.Target
// Only generate conversions for packages which explicitly request it
// by specifying one or more "+k8s:conversion-gen=<peer-pkg>"
// in their doc.go file.
peerPkgs := extractTag(pkg.Comments)
peerPkgs, err := extractTag(pkg.Comments)
if peerPkgs == nil {
klog.V(3).Infof(" no tag")
continue
}
if err != nil {
klog.Errorf("failed to extract tag %s", err)
continue
}
klog.V(3).Infof(" tags: %q", peerPkgs)
if len(peerPkgs) == 1 && peerPkgs[0] == "false" {
// If a single +k8s:conversion-gen=false tag is defined, we still want
@@ -250,7 +276,10 @@ func GetTargets(context *generator.Context, args *args.Args) []generator.Target
// if the external types are not in the same package where the
// conversion functions to be generated
externalTypesValues := extractExternalTypesTag(pkg.Comments)
externalTypesValues, err := extractExternalTypesTag(pkg.Comments)
if err != nil {
klog.Fatalf("Failed to extract external types tag for package %q: %v", i, err)
}
if externalTypesValues != nil {
if len(externalTypesValues) != 1 {
klog.Fatalf(" expect only one value for %q tag, got: %q", externalTypesTagName, externalTypesValues)
@@ -300,17 +329,17 @@ func GetTargets(context *generator.Context, args *args.Args) []generator.Target
for _, i := range filteredInputs {
klog.V(3).Infof("considering pkg %q", i)
pkg := context.Universe[i]
// typesPkg is where the versioned types are defined. Sometimes it is
// different from pkg. For example, kubernetes core/v1 types are defined
// in k8s.io/api/core/v1, while pkg is at pkg/api/v1.
typesPkg := pkg
// Add conversion and defaulting functions.
getManualConversionFunctions(context, pkg, manualConversions)
// Find the right input pkg, which might not be this one.
externalTypes := pkgToExternal[i]
typesPkg = context.Universe[externalTypes]
// typesPkg is where the versioned types are defined. Sometimes it is
// different from pkg. For example, kubernetes core/v1 types are defined
// in k8s.io/api/core/v1, while pkg is at pkg/api/v1.
typesPkg := context.Universe[externalTypes]
unsafeEquality := TypesEqual(memoryEquivalentTypes)
if args.SkipUnsafe {
@@ -337,7 +366,10 @@ func GetTargets(context *generator.Context, args *args.Args) []generator.Target
// If there is a manual conversion defined between two types, exclude it
// from being a candidate for unsafe conversion
for k, v := range manualConversions {
if isCopyOnly(v.CommentLines) {
copyOnly, err := isCopyOnly(v.CommentLines)
if err != nil {
klog.Errorf("error extracting tags: %v", err)
} else if copyOnly {
klog.V(4).Infof("Conversion function %s will not block memory copy because it is copy-only", v.Name)
continue
}
@@ -356,66 +388,85 @@ func (e equalMemoryTypes) Skip(a, b *types.Type) {
}
func (e equalMemoryTypes) Equal(a, b *types.Type) bool {
// alreadyVisitedTypes holds all the types that have already been checked in the structural type recursion.
alreadyVisitedTypes := make(map[*types.Type]bool)
return e.cachingEqual(a, b, alreadyVisitedTypes)
equal, _ := e.cachingEqual(a, b, nil)
return equal
}
func (e equalMemoryTypes) cachingEqual(a, b *types.Type, alreadyVisitedTypes map[*types.Type]bool) bool {
// cachingEqual recursively compares a and b for memory equality,
// using a cache of previously computed results, and caching the result before returning when possible.
// alreadyVisitedStack is used to check for cycles during recursion.
// The returned cacheable boolean tells the caller whether the equal result is a definitive answer that can be safely cached,
// or if it's a temporary assumption made to break a cycle in a recursively defined type.
func (e equalMemoryTypes) cachingEqual(a, b *types.Type, alreadyVisitedStack []*types.Type) (equal, cacheable bool) {
if a == b {
return true
return true, true
}
if equal, ok := e[conversionPair{a, b}]; ok {
return equal
return equal, true
}
if equal, ok := e[conversionPair{b, a}]; ok {
return equal
return equal, true
}
result := e.equal(a, b, alreadyVisitedTypes)
e[conversionPair{a, b}] = result
e[conversionPair{b, a}] = result
return result
result, cacheable := e.equal(a, b, alreadyVisitedStack)
if cacheable {
e[conversionPair{a, b}] = result
e[conversionPair{b, a}] = result
}
return result, cacheable
}
func (e equalMemoryTypes) equal(a, b *types.Type, alreadyVisitedTypes map[*types.Type]bool) bool {
// equal recursively compares a and b for memory equality.
// alreadyVisitedStack is used to check for cycles during recursion.
// The returned cacheable boolean tells the caller whether the equal result is a definitive answer that can be safely cached,
// or if it's a temporary assumption made to break a cycle in a recursively defined type.
func (e equalMemoryTypes) equal(a, b *types.Type, alreadyVisitedStack []*types.Type) (equal, cacheable bool) {
in, out := unwrapAlias(a), unwrapAlias(b)
switch {
case in == out:
return true
return true, true
case in.Kind == out.Kind:
// if the type exists already, return early to avoid recursion
if alreadyVisitedTypes[in] {
return true
for _, v := range alreadyVisitedStack {
if v == in {
// if the type was visited in this stack already, return early to avoid infinite recursion, but do not cache the results
return true, false
}
}
alreadyVisitedTypes[in] = true
alreadyVisitedStack = append(alreadyVisitedStack, in)
switch in.Kind {
case types.Struct:
if len(in.Members) != len(out.Members) {
return false
return false, true
}
cacheable = true
for i, inMember := range in.Members {
outMember := out.Members[i]
if !e.cachingEqual(inMember.Type, outMember.Type, alreadyVisitedTypes) {
return false
memberEqual, memberCacheable := e.cachingEqual(inMember.Type, outMember.Type, alreadyVisitedStack)
if !memberEqual {
return false, true
}
if !memberCacheable {
cacheable = false
}
}
return true
return true, cacheable
case types.Pointer:
return e.cachingEqual(in.Elem, out.Elem, alreadyVisitedTypes)
return e.cachingEqual(in.Elem, out.Elem, alreadyVisitedStack)
case types.Map:
return e.cachingEqual(in.Key, out.Key, alreadyVisitedTypes) && e.cachingEqual(in.Elem, out.Elem, alreadyVisitedTypes)
keyEqual, keyCacheable := e.cachingEqual(in.Key, out.Key, alreadyVisitedStack)
valueEqual, valueCacheable := e.cachingEqual(in.Elem, out.Elem, alreadyVisitedStack)
return keyEqual && valueEqual, keyCacheable && valueCacheable
case types.Slice:
return e.cachingEqual(in.Elem, out.Elem, alreadyVisitedTypes)
return e.cachingEqual(in.Elem, out.Elem, alreadyVisitedStack)
case types.Interface:
// TODO: determine whether the interfaces are actually equivalent - for now, they must have the
// same type.
return false
return false, true
case types.Builtin:
return in.Name.Name == out.Name.Name
return in.Name.Name == out.Name.Name, true
}
}
return false
return false, true
}
func findMember(t *types.Type, name string) (types.Member, bool) {
@@ -520,7 +571,12 @@ func (g *genConversion) convertibleOnlyWithinPackage(inType, outType *types.Type
return false
}
// If the type has opted out, skip it.
tagvals := extractTag(t.CommentLines)
tagvals, err := extractTag(t.CommentLines)
if err != nil {
klog.Errorf("Type %v: error extracting tags: %v", t, err)
return false
}
if tagvals != nil {
if tagvals[0] != "false" {
klog.Fatalf("Type %v: unsupported %s value: %q", t, tagName, tagvals[0])
@@ -542,8 +598,12 @@ func (g *genConversion) convertibleOnlyWithinPackage(inType, outType *types.Type
func getExplicitFromTypes(t *types.Type) []types.Name {
comments := t.SecondClosestCommentLines
comments = append(comments, t.CommentLines...)
paths := extractExplicitFromTag(comments)
result := []types.Name{}
paths, err := extractExplicitFromTag(comments)
if err != nil {
klog.Errorf("Error extracting explicit-from tag for %v: %v", t.Name, err)
return result
}
for _, path := range paths {
items := strings.Split(path, ".")
if len(items) != 2 {
@@ -869,7 +929,11 @@ func (g *genConversion) doSlice(inType, outType *types.Type, sw *generator.Snipp
func (g *genConversion) doStruct(inType, outType *types.Type, sw *generator.SnippetWriter) {
for _, inMember := range inType.Members {
if tagvals := extractTag(inMember.CommentLines); tagvals != nil && tagvals[0] == "false" {
tagvals, err := extractTag(inMember.CommentLines)
if err != nil {
klog.Errorf("Member %v.%v: error extracting tags: %v", inType, inMember.Name, err)
}
if tagvals != nil && tagvals[0] == "false" {
// This field is excluded from conversion.
sw.Do("// INFO: in."+inMember.Name+" opted out of conversion generation\n", nil)
continue
@@ -918,7 +982,10 @@ func (g *genConversion) doStruct(inType, outType *types.Type, sw *generator.Snip
// check based on the top level name, not the underlying names
if function, ok := g.preexists(inMember.Type, outMember.Type); ok {
if isDrop(function.CommentLines) {
dropFn, err := isDrop(function.CommentLines)
if err != nil {
klog.Errorf("Error extracting drop tag for function %s: %v", function.Name, err)
} else if dropFn {
continue
}
// copy-only functions that are directly assignable can be inlined instead of invoked.
@@ -926,7 +993,12 @@ func (g *genConversion) doStruct(inType, outType *types.Type, sw *generator.Snip
// correctly copied between types. These functions are equivalent to a memory assignment,
// and are necessary for the reflection path, but should not block memory conversion.
// Convert_unversioned_Time_to_unversioned_Time is an example of this logic.
if !isCopyOnly(function.CommentLines) || !g.isFastConversion(inMemberType, outMemberType) {
copyOnly, copyErr := isCopyOnly(function.CommentLines)
if copyErr != nil {
klog.Errorf("Error extracting copy-only tag for function %s: %v", function.Name, copyErr)
copyOnly = false
}
if !copyOnly || !g.isFastConversion(inMemberType, outMemberType) {
args["function"] = function
sw.Do("if err := $.function|raw$(&in.$.name$, &out.$.name$, s); err != nil {\n", args)
sw.Do("return err\n", nil)
@@ -1080,7 +1152,11 @@ func (g *genConversion) generateFromURLValues(inType, outType *types.Type, sw *g
}
sw.Do("func auto"+nameTmpl+"(in *$.inType|raw$, out *$.outType|raw$, s $.Scope|raw$) error {\n", args)
for _, outMember := range outType.Members {
if tagvals := extractTag(outMember.CommentLines); tagvals != nil && tagvals[0] == "false" {
tagvals, err := extractTag(outMember.CommentLines)
if err != nil {
klog.Errorf("Member %v.%v: error extracting tags: %v", outType, outMember.Name, err)
}
if tagvals != nil && tagvals[0] == "false" {
// This field is excluded from conversion.
sw.Do("// INFO: in."+outMember.Name+" opted out of conversion generation\n", nil)
continue

View File

@@ -24,6 +24,7 @@ import (
"strings"
"k8s.io/code-generator/cmd/deepcopy-gen/args"
genutil "k8s.io/code-generator/pkg/util"
"k8s.io/gengo/v2"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
@@ -53,21 +54,24 @@ func extractEnabledTypeTag(t *types.Type) *enabledTagValue {
}
func extractEnabledTag(comments []string) *enabledTagValue {
tagVals := gengo.ExtractCommentTags("+", comments)[tagEnabledName]
if tagVals == nil {
tags, err := genutil.ExtractCommentTagsWithoutArguments("+", []string{tagEnabledName}, comments)
if err != nil {
klog.Fatalf("Error extracting %s tags: %v", tagEnabledName, err)
}
if tags[tagEnabledName] == nil {
// No match for the tag.
return nil
}
// If there are multiple values, abort.
if len(tagVals) > 1 {
klog.Fatalf("Found %d %s tags: %q", len(tagVals), tagEnabledName, tagVals)
if len(tags[tagEnabledName]) > 1 {
klog.Fatalf("Found %d %s tags: %q", len(tags[tagEnabledName]), tagEnabledName, tags[tagEnabledName])
}
// If we got here we are returning something.
tag := &enabledTagValue{}
// Get the primary value.
parts := strings.Split(tagVals[0], ",")
parts := strings.Split(tags[tagEnabledName][0], ",")
if len(parts) >= 1 {
tag.value = parts[0]
}
@@ -451,8 +455,11 @@ func (g *genDeepCopy) needsGeneration(t *types.Type) bool {
func extractInterfacesTag(t *types.Type) []string {
var result []string
comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...)
values := gengo.ExtractCommentTags("+", comments)[interfacesTagName]
for _, v := range values {
tags, err := genutil.ExtractCommentTagsWithoutArguments("+", []string{interfacesTagName}, comments)
if err != nil {
klog.Fatalf("Error extracting %s tags: %v", interfacesTagName, err)
}
for _, v := range tags[interfacesTagName] {
if len(v) == 0 {
continue
}
@@ -469,7 +476,12 @@ func extractInterfacesTag(t *types.Type) []string {
func extractNonPointerInterfaces(t *types.Type) (bool, error) {
comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...)
values := gengo.ExtractCommentTags("+", comments)[interfacesNonPointerTagName]
tags, err := genutil.ExtractCommentTagsWithoutArguments("+", []string{interfacesNonPointerTagName}, comments)
if err != nil {
return false, fmt.Errorf("failed to parse comments: %w", err)
}
values := tags[interfacesNonPointerTagName]
if len(values) == 0 {
return false, nil
}

View File

@@ -28,6 +28,7 @@ import (
"strings"
"k8s.io/code-generator/cmd/defaulter-gen/args"
genutil "k8s.io/code-generator/pkg/util"
"k8s.io/gengo/v2"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
@@ -64,24 +65,40 @@ const tagName = "k8s:defaulter-gen"
const inputTagName = "k8s:defaulter-gen-input"
const defaultTagName = "default"
func extractDefaultTag(comments []string) []string {
return gengo.ExtractCommentTags("+", comments)[defaultTagName]
}
func extractTag(comments []string) []string {
return gengo.ExtractCommentTags("+", comments)[tagName]
}
func extractInputTag(comments []string) []string {
return gengo.ExtractCommentTags("+", comments)[inputTagName]
}
func checkTag(comments []string, require ...string) bool {
values := gengo.ExtractCommentTags("+", comments)[tagName]
if len(require) == 0 {
return len(values) == 1 && values[0] == ""
func extractDefaultTag(comments []string) ([]string, error) {
tags, err := genutil.ExtractCommentTagsWithoutArguments("+", []string{defaultTagName}, comments)
if err != nil {
return nil, err
}
return reflect.DeepEqual(values, require)
return tags[defaultTagName], nil
}
func extractTag(comments []string) ([]string, error) {
tags, err := genutil.ExtractCommentTagsWithoutArguments("+", []string{tagName}, comments)
if err != nil {
return nil, err
}
return tags[tagName], nil
}
func extractInputTag(comments []string) ([]string, error) {
tags, err := genutil.ExtractCommentTagsWithoutArguments("+", []string{inputTagName}, comments)
if err != nil {
return nil, err
}
return tags[inputTagName], nil
}
func checkTag(comments []string, require ...string) (bool, error) {
tags, err := genutil.ExtractCommentTagsWithoutArguments("+", []string{tagName}, comments)
if err != nil {
return false, err
}
if len(require) == 0 {
return len(tags[tagName]) == 1 && tags[tagName][0] == "", nil
}
return reflect.DeepEqual(tags[tagName], require), nil
}
func defaultFnNamer() *namer.NameStrategy {
@@ -246,7 +263,10 @@ func GetTargets(context *generator.Context, args *args.Args) []generator.Target
pkg := context.Universe[i]
// if the types are not in the same package where the defaulter functions to be generated
inputTags := extractInputTag(pkg.Comments)
inputTags, err := extractInputTag(pkg.Comments)
if err != nil {
panic(fmt.Sprintf("error extracting input tag: %v", err))
}
if len(inputTags) > 1 {
panic(fmt.Sprintf("there may only be one input tag, got %#v", inputTags))
}
@@ -310,7 +330,10 @@ func GetTargets(context *generator.Context, args *args.Args) []generator.Target
getManualDefaultingFunctions(context, context.Universe[pp], existingDefaulters)
}
typesWith := extractTag(pkg.Comments)
typesWith, err := extractTag(pkg.Comments)
if err != nil {
klog.Fatalf("Error extracting %s tag: %v", tagName, err)
}
shouldCreateObjectDefaulterFn := func(t *types.Type) bool {
if defaults, ok := existingDefaulters[t]; ok && defaults.object != nil {
// A default generator is defined
@@ -322,11 +345,19 @@ func GetTargets(context *generator.Context, args *args.Args) []generator.Target
return false
}
// opt-out
if checkTag(t.SecondClosestCommentLines, "false") {
optOut, err := checkTag(t.SecondClosestCommentLines, "false")
if err != nil {
klog.Fatalf("Error extracting %s tags: %v", tagName, err)
}
if optOut {
return false
}
// opt-in
if checkTag(t.SecondClosestCommentLines, "true") {
optIn, err := checkTag(t.SecondClosestCommentLines, "true")
if err != nil {
klog.Fatalf("Error extracting %s tags: %v", tagName, err)
}
if optIn {
return true
}
// For every k8s:defaulter-gen tag at the package level, interpret the value as a
@@ -497,13 +528,16 @@ func getPointerElementPath(t *types.Type) []*types.Type {
}
// getNestedDefault returns the first default value when resolving alias types
func getNestedDefault(t *types.Type) string {
func getNestedDefault(t *types.Type) (string, error) {
var prev *types.Type
for prev != t {
prev = t
defaultMap := extractDefaultTag(t.CommentLines)
defaultMap, err := extractDefaultTag(t.CommentLines)
if err != nil {
return "", err
}
if len(defaultMap) == 1 && defaultMap[0] != "" {
return defaultMap[0]
return defaultMap[0], nil
}
if t.Kind == types.Alias {
t = t.Underlying
@@ -511,7 +545,7 @@ func getNestedDefault(t *types.Type) string {
t = t.Elem
}
}
return ""
return "", nil
}
var refRE = regexp.MustCompile(`^ref\((?P<reference>[^"]+)\)$`)
@@ -538,7 +572,11 @@ func parseSymbolReference(s, sourcePackage string) (types.Name, bool) {
}
func populateDefaultValue(node *callNode, t *types.Type, tags string, commentLines []string, commentPackage string) *callNode {
defaultMap := extractDefaultTag(commentLines)
defaultMap, err := extractDefaultTag(commentLines)
if err != nil {
klog.Fatalf("Error extracting default tag: %v", err)
}
var defaultString string
if len(defaultMap) == 1 {
defaultString = defaultMap[0]
@@ -548,7 +586,10 @@ func populateDefaultValue(node *callNode, t *types.Type, tags string, commentLin
baseT, depth := resolveTypeAndDepth(t)
if depth > 0 && defaultString == "" {
defaultString = getNestedDefault(t)
defaultString, err = getNestedDefault(t)
if err != nil {
klog.Fatalf("Error extracting nested default tag: %v", err)
}
}
if len(defaultString) == 0 {
@@ -622,7 +663,11 @@ func (c *callTreeForType) build(t *types.Type, root bool) *callNode {
parent.call = append(parent.call, defaults.base)
// if the base function indicates it "covers" (it already includes defaulters)
// we can halt recursion
if checkTag(defaults.base.CommentLines, "covers") {
isCovers, err := checkTag(defaults.base.CommentLines, "covers")
if err != nil {
klog.Fatalf("error extracting %s tag: %v", tagName, err)
}
if isCovers {
klog.V(6).Infof("the defaulter %s indicates it covers all sub generators", t.Name)
return parent
}

View File

@@ -25,6 +25,7 @@ import (
"strconv"
"strings"
genutil "k8s.io/code-generator/pkg/util"
"k8s.io/gengo/v2"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
@@ -80,17 +81,20 @@ func (g *genProtoIDL) Namers(c *generator.Context) namer.NameSystems {
// Filter ignores types that are identified as not exportable.
func (g *genProtoIDL) Filter(c *generator.Context, t *types.Type) bool {
tagVals := gengo.ExtractCommentTags("+", t.CommentLines)["protobuf"]
if tagVals != nil {
if tagVals[0] == "false" {
tags, err := genutil.ExtractCommentTagsWithoutArguments("+", []string{"protobuf"}, t.CommentLines)
if err != nil {
klog.Fatalf(`Error extracting tag "protobuf": %v`, err)
}
if tags["protobuf"] != nil {
if tags["protobuf"][0] == "false" {
// Type specified "false".
return false
}
if tagVals[0] == "true" {
if tags["protobuf"][0] == "true" {
// Type specified "true".
return true
}
klog.Fatalf(`Comment tag "protobuf" must be true or false, found: %q`, tagVals[0])
klog.Fatalf(`Comment tag "protobuf" must be true or false, found: %q`, tags["protobuf"][0])
}
if !g.generateAll {
// We're not generating everything.

View File

@@ -144,15 +144,23 @@ func GetTargets(context *generator.Context, args *args.Args) []generator.Target
// If there's a comment of the form "// +groupName=somegroup" or
// "// +groupName=somegroup.foo.bar.io", use the first field (somegroup) as the name of the
// group when generating.
if override := gengo.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil {
gv.Group = clientgentypes.Group(override[0])
override, err := genutil.ExtractCommentTagsWithoutArguments("+", []string{"groupName"}, p.Comments)
if err != nil {
klog.Fatalf("error extracting groupName tags: %v", err)
}
if override["groupName"] != nil {
gv.Group = clientgentypes.Group(override["groupName"][0])
}
// If there's a comment of the form "// +groupGoName=SomeUniqueShortName", use that as
// the Go group identifier in CamelCase. It defaults
groupGoNames[groupPackageName] = namer.IC(strings.Split(gv.Group.NonEmpty(), ".")[0])
if override := gengo.ExtractCommentTags("+", p.Comments)["groupGoName"]; override != nil {
groupGoNames[groupPackageName] = namer.IC(override[0])
override, err = genutil.ExtractCommentTagsWithoutArguments("+", []string{"groupGoName"}, p.Comments)
if err != nil {
klog.Fatalf("error extracting groupGoName tags: %v", err)
}
if override["groupGoName"] != nil {
groupGoNames[groupPackageName] = namer.IC(override["groupGoName"][0])
}
var typesToGenerate []*types.Type

View File

@@ -26,6 +26,7 @@ import (
"k8s.io/code-generator/cmd/client-gen/generators/util"
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
"k8s.io/code-generator/cmd/lister-gen/args"
genutil "k8s.io/code-generator/pkg/util"
"k8s.io/gengo/v2"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
@@ -101,8 +102,12 @@ func GetTargets(context *generator.Context, args *args.Args) []generator.Target
// If there's a comment of the form "// +groupName=somegroup" or
// "// +groupName=somegroup.foo.bar.io", use the first field (somegroup) as the name of the
// group when generating.
if override := gengo.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil {
gv.Group = clientgentypes.Group(strings.SplitN(override[0], ".", 2)[0])
override, err := genutil.ExtractCommentTagsWithoutArguments("+", []string{"groupName"}, p.Comments)
if err != nil {
klog.Fatalf("error extracting groupName tags: %v", err)
}
if override["groupName"] != nil {
gv.Group = clientgentypes.Group(strings.SplitN(override["groupName"][0], ".", 2)[0])
}
var typesToGenerate []*types.Type

View File

@@ -26,6 +26,7 @@ import (
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
"k8s.io/code-generator/cmd/register-gen/args"
genutil "k8s.io/code-generator/pkg/util"
"k8s.io/gengo/v2"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
@@ -83,8 +84,13 @@ func GetTargets(context *generator.Context, args *args.Args) []generator.Target
// if there is a comment of the form "// +groupName=somegroup" or "// +groupName=somegroup.foo.bar.io",
// extract the fully qualified API group name from it and overwrite the group inferred from the package path
if override := gengo.ExtractCommentTags("+", pkg.Comments)["groupName"]; override != nil {
groupName := override[0]
override, err := genutil.ExtractCommentTagsWithoutArguments("+", []string{"groupName"}, pkg.Comments)
if err != nil {
klog.Errorf("error extracting groupName tags: %v", err)
continue
}
if override["groupName"] != nil {
groupName := override["groupName"][0]
klog.V(5).Infof("overriding the group name with = %s", groupName)
gv.Group = clientgentypes.Group(groupName)
}

View File

@@ -32,6 +32,20 @@ KUBE_CODEGEN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
# before sourcing this file.
CODEGEN_VERSION_SPEC="${KUBE_CODEGEN_TAG:+"@${KUBE_CODEGEN_TAG}"}"
# Go installs in $GOBIN if defined, and $GOPATH/bin otherwise. We want to know
# which one it is, so we can use it later.
function get_gobin() {
local from_env
from_env="$(go env GOBIN)"
if [[ -n "${from_env}" ]]; then
echo "${from_env}"
else
echo "$(go env GOPATH)/bin"
fi
}
GOBIN="$(get_gobin)"
export GOBIN
function kube::codegen::internal::findz() {
# We use `find` rather than `git ls-files` because sometimes external
# projects use this across repos. This is an imperfect wrapper of find,
@@ -116,8 +130,6 @@ function kube::codegen::gen_helpers() {
# shellcheck disable=2046 # printf word-splitting is intentional
GO111MODULE=on go install $(printf "k8s.io/code-generator/cmd/%s " "${BINS[@]}")
)
# Go installs in $GOBIN if defined, and $GOPATH/bin otherwise
gobin="${GOBIN:-$(go env GOPATH)/bin}"
# Deepcopy
#
@@ -144,7 +156,7 @@ function kube::codegen::gen_helpers() {
-name zz_generated.deepcopy.go \
| xargs -0 rm -f
"${gobin}/deepcopy-gen" \
"${GOBIN}/deepcopy-gen" \
-v "${v}" \
--output-file zz_generated.deepcopy.go \
--go-header-file "${boilerplate}" \
@@ -176,7 +188,7 @@ function kube::codegen::gen_helpers() {
-name zz_generated.validations.go \
| xargs -0 rm -f
"${gobin}/validation-gen" \
"${GOBIN}/validation-gen" \
-v "${v}" \
--output-file zz_generated.validations.go \
--go-header-file "${boilerplate}" \
@@ -208,7 +220,7 @@ function kube::codegen::gen_helpers() {
-name zz_generated.defaults.go \
| xargs -0 rm -f
"${gobin}/defaulter-gen" \
"${GOBIN}/defaulter-gen" \
-v "${v}" \
--output-file zz_generated.defaults.go \
--go-header-file "${boilerplate}" \
@@ -244,7 +256,7 @@ function kube::codegen::gen_helpers() {
for arg in "${extra_peers[@]:+"${extra_peers[@]}"}"; do
extra_peer_args+=("--extra-peer-dirs" "$arg")
done
"${gobin}/conversion-gen" \
"${GOBIN}/conversion-gen" \
-v "${v}" \
--output-file zz_generated.conversion.go \
--go-header-file "${boilerplate}" \
@@ -367,8 +379,6 @@ function kube::codegen::gen_openapi() {
# shellcheck disable=2046 # printf word-splitting is intentional
GO111MODULE=on go install $(printf "k8s.io/kube-openapi/cmd/%s " "${BINS[@]}")
)
# Go installs in $GOBIN if defined, and $GOPATH/bin otherwise
gobin="${GOBIN:-$(go env GOPATH)/bin}"
local input_pkgs=( "${extra_pkgs[@]:+"${extra_pkgs[@]}"}")
while read -r dir; do
@@ -393,7 +403,7 @@ function kube::codegen::gen_openapi() {
-name zz_generated.openapi.go \
| xargs -0 rm -f
"${gobin}/openapi-gen" \
"${GOBIN}/openapi-gen" \
-v "${v}" \
--output-file zz_generated.openapi.go \
--go-header-file "${boilerplate}" \
@@ -600,8 +610,6 @@ function kube::codegen::gen_client() {
# shellcheck disable=2046 # printf word-splitting is intentional
GO111MODULE=on go install $(printf "k8s.io/code-generator/cmd/%s " "${BINS[@]}")
)
# Go installs in $GOBIN if defined, and $GOPATH/bin otherwise
gobin="${GOBIN:-$(go env GOPATH)/bin}"
local group_versions=()
local input_pkgs=()
@@ -642,7 +650,7 @@ function kube::codegen::gen_client() {
|| true \
) | xargs -0 rm -f
"${gobin}/applyconfiguration-gen" \
"${GOBIN}/applyconfiguration-gen" \
-v "${v}" \
--go-header-file "${boilerplate}" \
--output-dir "${out_dir}/${applyconfig_subdir}" \
@@ -665,7 +673,7 @@ function kube::codegen::gen_client() {
for arg in "${group_versions[@]}"; do
inputs+=("--input" "$arg")
done
"${gobin}/client-gen" \
"${GOBIN}/client-gen" \
-v "${v}" \
--go-header-file "${boilerplate}" \
--output-dir "${out_dir}/${clientset_subdir}" \
@@ -687,7 +695,7 @@ function kube::codegen::gen_client() {
|| true \
) | xargs -0 rm -f
"${gobin}/lister-gen" \
"${GOBIN}/lister-gen" \
-v "${v}" \
--go-header-file "${boilerplate}" \
--output-dir "${out_dir}/${listers_subdir}" \
@@ -704,7 +712,7 @@ function kube::codegen::gen_client() {
|| true \
) | xargs -0 rm -f
"${gobin}/informer-gen" \
"${GOBIN}/informer-gen" \
-v "${v}" \
--go-header-file "${boilerplate}" \
--output-dir "${out_dir}/${informers_subdir}" \
@@ -772,8 +780,6 @@ function kube::codegen::gen_register() {
# shellcheck disable=2046 # printf word-splitting is intentional
GO111MODULE=on go install $(printf "k8s.io/code-generator/cmd/%s " "${BINS[@]}")
)
# Go installs in $GOBIN if defined, and $GOPATH/bin otherwise
gobin="${GOBIN:-$(go env GOPATH)/bin}"
# Register
#
@@ -800,7 +806,7 @@ function kube::codegen::gen_register() {
-name zz_generated.register.go \
| xargs -0 rm -f
"${gobin}/register-gen" \
"${GOBIN}/register-gen" \
-v "${v}" \
--output-file zz_generated.register.go \
--go-header-file "${boilerplate}" \

92
vendor/k8s.io/code-generator/pkg/util/comments.go generated vendored Normal file
View File

@@ -0,0 +1,92 @@
/*
Copyright 2025 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"fmt"
"k8s.io/gengo/v2"
)
// ExtractCommentTagsWithoutArguments parses comments for special metadata tags. The
// marker argument should be unique enough to identify the tags needed, and
// should not be a marker for tags you don't want, or else the caller takes
// responsibility for making that distinction.
//
// The tagNames argument is a list of specific tags being extracted. If this is
// nil or empty, all lines which match the marker are considered. If this is
// specified, only lines with begin with marker + one of the tags will be
// considered. This is useful when a common marker is used which may match
// lines which fail this syntax (e.g. which predate this definition).
//
// This function looks for input lines of the following forms:
// - 'marker' + "key=value"
// - 'marker' + "key()=value"
// - 'marker' + "key(arg)=value"
//
// The arg is forbidden. This function only consider tags with no arguments specified
// (either as "key=value" or as // "key()=value"). Finding tags with an argument will
// result in an error.
//
// The value is optional. If not specified, the resulting Tag will have "" as
// the value.
//
// Tag comment-lines may have a trailing end-of-line comment.
//
// The map returned here is keyed by the Tag's name without args.
//
// A tag can be specified more than one time and all values are returned. If
// the resulting map has an entry for a key, the value (a slice) is guaranteed
// to have at least 1 element.
//
// Example: if you pass "+" for 'marker', and the following lines are in
// the comments:
//
// +foo=val1 // foo
// +bar
// +foo=val2 // also foo
// +foo()=val3 // still foo
// +baz="qux"
//
// Then this function will return:
//
// map[string][]string{"foo":{"val1", "val2", "val3"}, "bar": {""}, "baz": {`"qux"`}}
func ExtractCommentTagsWithoutArguments(marker string, tagNames []string, lines []string) (map[string][]string, error) {
functionStyleTags, err := gengo.ExtractFunctionStyleCommentTags(marker, tagNames, lines)
if err != nil {
return nil, err
}
out := make(map[string][]string)
for tagName, tags := range functionStyleTags {
values := make([]string, 0)
for _, tag := range tags {
if tag.Args == nil {
values = append(values, tag.Value)
} else {
return nil, fmt.Errorf(`failed to parse tag %s: expected no arguments, found "%s"`, tag, tag.Args[0])
}
}
if len(values) > 0 {
out[tagName] = values
}
}
return out, nil
}