update dependencies (#6267)

Signed-off-by: hongming <coder.scala@gmail.com>
This commit is contained in:
hongming
2024-11-06 10:27:06 +08:00
committed by GitHub
parent faf255a084
commit cfebd96a1f
4263 changed files with 341374 additions and 132036 deletions

View File

@@ -18,19 +18,22 @@ package args
import (
"fmt"
"path"
"github.com/spf13/pflag"
"k8s.io/gengo/args"
"k8s.io/code-generator/cmd/client-gen/types"
codegenutil "k8s.io/code-generator/pkg/util"
)
var DefaultInputDirs = []string{}
type Args struct {
// The directory for the generated results.
OutputDir string
// The Go import-path of the generated results.
OutputPkg string
// The boilerplate header for Go files.
GoHeaderFile string
// CustomArgs is a wrapper for arguments to client-gen.
type CustomArgs struct {
// A sorted list of group versions to generate. For each of them the package path is found
// in GroupVersionToInputPath.
Groups []types.GroupVersions
@@ -53,69 +56,75 @@ type CustomArgs struct {
// For example 'Endpoints:Endpoints', otherwise the pluralizer will generate 'Endpointes'.
PluralExceptions []string
// ApplyConfigurationPackage is the package of apply builders generated by typebuilder-gen.
// ApplyConfigurationPackage is the package of apply builders generated by
// applyconfiguration-gen.
// If non-empty, Apply functions are generated for each type and reference the apply builders.
// If empty (""), Apply functions are not generated.
ApplyConfigurationPackage string
}
func NewDefaults() (*args.GeneratorArgs, *CustomArgs) {
genericArgs := args.Default().WithoutDefaultFlagParsing()
customArgs := &CustomArgs{
func New() *Args {
return &Args{
ClientsetName: "internalclientset",
ClientsetAPIPath: "/apis",
ClientsetOnly: false,
FakeClient: true,
PluralExceptions: []string{"Endpoints:Endpoints"},
ApplyConfigurationPackage: "",
}
genericArgs.CustomArgs = customArgs
genericArgs.InputDirs = DefaultInputDirs
if pkg := codegenutil.CurrentPackage(); len(pkg) != 0 {
genericArgs.OutputPackagePath = path.Join(pkg, "pkg/client/clientset")
}
return genericArgs, customArgs
}
func (ca *CustomArgs) AddFlags(fs *pflag.FlagSet, inputBase string) {
gvsBuilder := NewGroupVersionsBuilder(&ca.Groups)
pflag.Var(NewGVPackagesValue(gvsBuilder, nil), "input", "group/versions that client-gen will generate clients for. At most one version per group is allowed. Specified in the format \"group1/version1,group2/version2...\".")
pflag.Var(NewGVTypesValue(&ca.IncludedTypesOverrides, []string{}), "included-types-overrides", "list of group/version/type for which client should be generated. By default, client is generated for all types which have genclient in types.go. This overrides that. For each groupVersion in this list, only the types mentioned here will be included. The default check of genclient will be used for other group versions.")
pflag.Var(NewInputBasePathValue(gvsBuilder, inputBase), "input-base", "base path to look for the api group.")
pflag.StringVarP(&ca.ClientsetName, "clientset-name", "n", ca.ClientsetName, "the name of the generated clientset package.")
pflag.StringVarP(&ca.ClientsetAPIPath, "clientset-api-path", "", ca.ClientsetAPIPath, "the value of default API HTTP path, starting with / and without trailing /.")
pflag.BoolVar(&ca.ClientsetOnly, "clientset-only", ca.ClientsetOnly, "when set, client-gen only generates the clientset shell, without generating the individual typed clients")
pflag.BoolVar(&ca.FakeClient, "fake-clientset", ca.FakeClient, "when set, client-gen will generate the fake clientset that can be used in tests")
fs.StringSliceVar(&ca.PluralExceptions, "plural-exceptions", ca.PluralExceptions, "list of comma separated plural exception definitions in Type:PluralizedType form")
fs.StringVar(&ca.ApplyConfigurationPackage, "apply-configuration-package", ca.ApplyConfigurationPackage, "optional package of apply configurations, generated by applyconfiguration-gen, that are required to generate Apply functions for each type in the clientset. By default Apply functions are not generated.")
func (args *Args) AddFlags(fs *pflag.FlagSet, inputBase string) {
gvsBuilder := NewGroupVersionsBuilder(&args.Groups)
fs.StringVar(&args.OutputDir, "output-dir", "",
"the base directory under which to generate results")
fs.StringVar(&args.OutputPkg, "output-pkg", args.OutputPkg,
"the Go import-path of the generated results")
fs.StringVar(&args.GoHeaderFile, "go-header-file", "",
"the path to a file containing boilerplate header text; the string \"YEAR\" will be replaced with the current 4-digit year")
fs.Var(NewGVPackagesValue(gvsBuilder, nil), "input",
"group/versions that client-gen will generate clients for. At most one version per group is allowed. Specified in the format \"group1/version1,group2/version2...\".")
fs.Var(NewGVTypesValue(&args.IncludedTypesOverrides, []string{}), "included-types-overrides",
"list of group/version/type for which client should be generated. By default, client is generated for all types which have genclient in types.go. This overrides that. For each groupVersion in this list, only the types mentioned here will be included. The default check of genclient will be used for other group versions.")
fs.Var(NewInputBasePathValue(gvsBuilder, inputBase), "input-base",
"base path to look for the api group.")
fs.StringVarP(&args.ClientsetName, "clientset-name", "n", args.ClientsetName,
"the name of the generated clientset package.")
fs.StringVarP(&args.ClientsetAPIPath, "clientset-api-path", "", args.ClientsetAPIPath,
"the value of default API HTTP path, starting with / and without trailing /.")
fs.BoolVar(&args.ClientsetOnly, "clientset-only", args.ClientsetOnly,
"when set, client-gen only generates the clientset shell, without generating the individual typed clients")
fs.BoolVar(&args.FakeClient, "fake-clientset", args.FakeClient,
"when set, client-gen will generate the fake clientset that can be used in tests")
fs.StringSliceVar(&args.PluralExceptions, "plural-exceptions", args.PluralExceptions,
"list of comma separated plural exception definitions in Type:PluralizedType form")
fs.StringVar(&args.ApplyConfigurationPackage, "apply-configuration-package", args.ApplyConfigurationPackage,
"optional package of apply configurations, generated by applyconfiguration-gen, that are required to generate Apply functions for each type in the clientset. By default Apply functions are not generated.")
// support old flags
fs.SetNormalizeFunc(mapFlagName("clientset-path", "output-package", fs.GetNormalizeFunc()))
fs.SetNormalizeFunc(mapFlagName("clientset-path", "output-pkg", fs.GetNormalizeFunc()))
}
func Validate(genericArgs *args.GeneratorArgs) error {
customArgs := genericArgs.CustomArgs.(*CustomArgs)
if len(genericArgs.OutputPackagePath) == 0 {
return fmt.Errorf("output package cannot be empty")
func (args *Args) Validate() error {
if len(args.OutputDir) == 0 {
return fmt.Errorf("--output-dir must be specified")
}
if len(customArgs.ClientsetName) == 0 {
return fmt.Errorf("clientset name cannot be empty")
if len(args.OutputPkg) == 0 {
return fmt.Errorf("--output-pkg must be specified")
}
if len(customArgs.ClientsetAPIPath) == 0 {
return fmt.Errorf("clientset API path cannot be empty")
if len(args.ClientsetName) == 0 {
return fmt.Errorf("--clientset-name must be specified")
}
if len(args.ClientsetAPIPath) == 0 {
return fmt.Errorf("--clientset-api-path cannot be empty")
}
return nil
}
// GroupVersionPackages returns a map from GroupVersion to the package with the types.go.
func (ca *CustomArgs) GroupVersionPackages() map[types.GroupVersion]string {
func (args *Args) GroupVersionPackages() map[types.GroupVersion]string {
res := map[types.GroupVersion]string{}
for _, pkg := range ca.Groups {
for _, pkg := range args.Groups {
for _, v := range pkg.Versions {
res[types.GroupVersion{Group: pkg.Group, Version: v.Version}] = v.Package
}

View File

@@ -129,7 +129,9 @@ func (p *groupVersionsBuilder) update() error {
versionPkg := types.PackageVersion{Package: path.Join(p.importBasePath, pth, gv.Group.NonEmpty(), gv.Version.String()), Version: gv.Version}
if group, ok := seenGroups[gv.Group]; ok {
seenGroups[gv.Group].Versions = append(group.Versions, versionPkg)
vers := group.Versions
vers = append(vers, versionPkg)
seenGroups[gv.Group].Versions = vers
} else {
seenGroups[gv.Group] = &types.GroupVersions{
PackageName: gv.Group.NonEmpty(),

View File

@@ -18,21 +18,21 @@ limitations under the License.
package generators
import (
"fmt"
"path"
"path/filepath"
"strings"
clientgenargs "k8s.io/code-generator/cmd/client-gen/args"
"k8s.io/code-generator/cmd/client-gen/args"
"k8s.io/code-generator/cmd/client-gen/generators/fake"
"k8s.io/code-generator/cmd/client-gen/generators/scheme"
"k8s.io/code-generator/cmd/client-gen/generators/util"
"k8s.io/code-generator/cmd/client-gen/path"
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/args"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/gengo/v2"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
"k8s.io/gengo/v2/types"
"k8s.io/klog/v2"
)
@@ -128,31 +128,35 @@ func DefaultNameSystem() string {
return "public"
}
func packageForGroup(gv clientgentypes.GroupVersion, typeList []*types.Type, clientsetPackage string, groupPackageName string, groupGoName string, apiPath string, srcTreePath string, inputPackage string, applyBuilderPackage string, boilerplate []byte) generator.Package {
groupVersionClientPackage := filepath.Join(clientsetPackage, "typed", strings.ToLower(groupPackageName), strings.ToLower(gv.Version.NonEmpty()))
return &generator.DefaultPackage{
PackageName: strings.ToLower(gv.Version.NonEmpty()),
PackagePath: groupVersionClientPackage,
HeaderText: boilerplate,
PackageDocumentation: []byte("// This package has the automatically generated typed clients.\n"),
// GeneratorFunc returns a list of generators. Each generator makes a
func targetForGroup(gv clientgentypes.GroupVersion, typeList []*types.Type, clientsetDir, clientsetPkg string, groupPkgName string, groupGoName string, apiPath string, inputPkg string, applyBuilderPkg string, boilerplate []byte) generator.Target {
subdir := []string{"typed", strings.ToLower(groupPkgName), strings.ToLower(gv.Version.NonEmpty())}
gvDir := filepath.Join(clientsetDir, filepath.Join(subdir...))
gvPkg := path.Join(clientsetPkg, path.Join(subdir...))
return &generator.SimpleTarget{
PkgName: strings.ToLower(gv.Version.NonEmpty()),
PkgPath: gvPkg,
PkgDir: gvDir,
HeaderComment: boilerplate,
PkgDocComment: []byte("// This package has the automatically generated typed clients.\n"),
// GeneratorsFunc returns a list of generators. Each generator makes a
// single file.
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
GeneratorsFunc: func(c *generator.Context) (generators []generator.Generator) {
generators = []generator.Generator{
// Always generate a "doc.go" file.
generator.DefaultGen{OptionalName: "doc"},
generator.GoGenerator{OutputFilename: "doc.go"},
}
// Since we want a file per type that we generate a client for, we
// have to provide a function for this.
for _, t := range typeList {
generators = append(generators, &genClientForType{
DefaultGen: generator.DefaultGen{
OptionalName: strings.ToLower(c.Namers["private"].Name(t)),
GoGenerator: generator.GoGenerator{
OutputFilename: strings.ToLower(c.Namers["private"].Name(t)) + ".go",
},
outputPackage: groupVersionClientPackage,
inputPackage: inputPackage,
clientsetPackage: clientsetPackage,
applyConfigurationPackage: applyBuilderPackage,
outputPackage: gvPkg,
inputPackage: inputPkg,
clientsetPackage: clientsetPkg,
applyConfigurationPackage: applyBuilderPkg,
group: gv.Group.NonEmpty(),
version: gv.Version.String(),
groupGoName: groupGoName,
@@ -162,12 +166,12 @@ func packageForGroup(gv clientgentypes.GroupVersion, typeList []*types.Type, cli
}
generators = append(generators, &genGroup{
DefaultGen: generator.DefaultGen{
OptionalName: groupPackageName + "_client",
GoGenerator: generator.GoGenerator{
OutputFilename: groupPkgName + "_client.go",
},
outputPackage: groupVersionClientPackage,
inputPackage: inputPackage,
clientsetPackage: clientsetPackage,
outputPackage: gvPkg,
inputPackage: inputPkg,
clientsetPackage: clientsetPkg,
group: gv.Group.NonEmpty(),
version: gv.Version.String(),
groupGoName: groupGoName,
@@ -176,11 +180,11 @@ func packageForGroup(gv clientgentypes.GroupVersion, typeList []*types.Type, cli
imports: generator.NewImportTracker(),
})
expansionFileName := "generated_expansion"
expansionFileName := "generated_expansion.go"
generators = append(generators, &genExpansion{
groupPackagePath: filepath.Join(srcTreePath, groupVersionClientPackage),
DefaultGen: generator.DefaultGen{
OptionalName: expansionFileName,
groupPackagePath: gvDir,
GoGenerator: generator.GoGenerator{
OutputFilename: expansionFileName,
},
types: typeList,
})
@@ -193,23 +197,23 @@ func packageForGroup(gv clientgentypes.GroupVersion, typeList []*types.Type, cli
}
}
func packageForClientset(customArgs *clientgenargs.CustomArgs, clientsetPackage string, groupGoNames map[clientgentypes.GroupVersion]string, boilerplate []byte) generator.Package {
return &generator.DefaultPackage{
PackageName: customArgs.ClientsetName,
PackagePath: clientsetPackage,
HeaderText: boilerplate,
// GeneratorFunc returns a list of generators. Each generator generates a
func targetForClientset(args *args.Args, clientsetDir, clientsetPkg string, groupGoNames map[clientgentypes.GroupVersion]string, boilerplate []byte) generator.Target {
return &generator.SimpleTarget{
PkgName: args.ClientsetName,
PkgPath: clientsetPkg,
PkgDir: clientsetDir,
HeaderComment: boilerplate,
// GeneratorsFunc returns a list of generators. Each generator generates a
// single file.
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
GeneratorsFunc: func(c *generator.Context) (generators []generator.Generator) {
generators = []generator.Generator{
&genClientset{
DefaultGen: generator.DefaultGen{
OptionalName: "clientset",
GoGenerator: generator.GoGenerator{
OutputFilename: "clientset.go",
},
groups: customArgs.Groups,
groups: args.Groups,
groupGoNames: groupGoNames,
clientsetPackage: clientsetPackage,
outputPackage: customArgs.ClientsetName,
clientsetPackage: clientsetPkg,
imports: generator.NewImportTracker(),
},
}
@@ -218,13 +222,14 @@ func packageForClientset(customArgs *clientgenargs.CustomArgs, clientsetPackage
}
}
func packageForScheme(customArgs *clientgenargs.CustomArgs, clientsetPackage string, srcTreePath string, groupGoNames map[clientgentypes.GroupVersion]string, boilerplate []byte) generator.Package {
schemePackage := filepath.Join(clientsetPackage, "scheme")
func targetForScheme(args *args.Args, clientsetDir, clientsetPkg string, groupGoNames map[clientgentypes.GroupVersion]string, boilerplate []byte) generator.Target {
schemeDir := filepath.Join(clientsetDir, "scheme")
schemePkg := path.Join(clientsetPkg, "scheme")
// create runtime.Registry for internal client because it has to know about group versions
internalClient := false
NextGroup:
for _, group := range customArgs.Groups {
for _, group := range args.Groups {
for _, v := range group.Versions {
if v.String() == "" {
internalClient = true
@@ -233,26 +238,27 @@ NextGroup:
}
}
return &generator.DefaultPackage{
PackageName: "scheme",
PackagePath: schemePackage,
HeaderText: boilerplate,
PackageDocumentation: []byte("// This package contains the scheme of the automatically generated clientset.\n"),
// GeneratorFunc returns a list of generators. Each generator generates a
return &generator.SimpleTarget{
PkgName: "scheme",
PkgPath: schemePkg,
PkgDir: schemeDir,
HeaderComment: boilerplate,
PkgDocComment: []byte("// This package contains the scheme of the automatically generated clientset.\n"),
// GeneratorsFunc returns a list of generators. Each generator generates a
// single file.
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
GeneratorsFunc: func(c *generator.Context) (generators []generator.Generator) {
generators = []generator.Generator{
// Always generate a "doc.go" file.
generator.DefaultGen{OptionalName: "doc"},
generator.GoGenerator{OutputFilename: "doc.go"},
&scheme.GenScheme{
DefaultGen: generator.DefaultGen{
OptionalName: "register",
GoGenerator: generator.GoGenerator{
OutputFilename: "register.go",
},
InputPackages: customArgs.GroupVersionPackages(),
OutputPackage: schemePackage,
OutputPath: filepath.Join(srcTreePath, schemePackage),
Groups: customArgs.Groups,
InputPackages: args.GroupVersionPackages(),
OutputPkg: schemePkg,
OutputPath: schemeDir,
Groups: args.Groups,
GroupGoNames: groupGoNames,
ImportTracker: generator.NewImportTracker(),
CreateRegistry: internalClient,
@@ -268,12 +274,12 @@ 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, customArgs *clientgenargs.CustomArgs) {
func applyGroupOverrides(universe types.Universe, args *args.Args) {
// 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 customArgs.GroupVersionPackages() {
p := universe.Package(genutil.Vendorless(inputDir))
if override := types.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil {
for gv, inputDir := range args.GroupVersionPackages() {
p := universe.Package(inputDir)
if override := gengo.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil {
newGV := clientgentypes.GroupVersion{
Group: clientgentypes.Group(override[0]),
Version: gv.Version,
@@ -282,9 +288,9 @@ func applyGroupOverrides(universe types.Universe, customArgs *clientgenargs.Cust
}
}
// Modify customArgs.Groups based on the groupName overrides.
newGroups := make([]clientgentypes.GroupVersions, 0, len(customArgs.Groups))
for _, gvs := range customArgs.Groups {
// Modify args.Groups based on the groupName overrides.
newGroups := make([]clientgentypes.GroupVersions, 0, len(args.Groups))
for _, gvs := range args.Groups {
gv := clientgentypes.GroupVersion{
Group: gvs.Group,
Version: gvs.Versions[0].Version, // we only need a version, and the first will do
@@ -302,37 +308,64 @@ func applyGroupOverrides(universe types.Universe, customArgs *clientgenargs.Cust
newGroups = append(newGroups, gvs)
}
}
customArgs.Groups = newGroups
args.Groups = newGroups
}
// Packages makes the client package definition.
func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
boilerplate, err := arguments.LoadGoBoilerplate()
// Because we try to assemble inputs from an input-base and a set of
// group-version arguments, sometimes that comes in as a filesystem path. This
// function rewrites them all as their canonical Go import-paths.
//
// TODO: Change this tool to just take inputs as Go "patterns" like every other
// gengo tool, then extract GVs from those.
func sanitizePackagePaths(context *generator.Context, args *args.Args) error {
for i := range args.Groups {
pkg := &args.Groups[i]
for j := range pkg.Versions {
ver := &pkg.Versions[j]
input := ver.Package
p := context.Universe[input]
if p == nil || p.Name == "" {
pkgs, err := context.FindPackages(input)
if err != nil {
return fmt.Errorf("can't find input package %q: %w", input, err)
}
p = context.Universe[pkgs[0]]
if p == nil {
return fmt.Errorf("can't find input package %q in universe", input)
}
ver.Package = p.Path
}
}
}
return nil
}
// GetTargets makes the client target definition.
func GetTargets(context *generator.Context, args *args.Args) []generator.Target {
boilerplate, err := gengo.GoBoilerplate(args.GoHeaderFile, "", gengo.StdGeneratedBy)
if err != nil {
klog.Fatalf("Failed loading boilerplate: %v", err)
}
customArgs, ok := arguments.CustomArgs.(*clientgenargs.CustomArgs)
if !ok {
klog.Fatalf("cannot convert arguments.CustomArgs to clientgenargs.CustomArgs")
}
includedTypesOverrides := customArgs.IncludedTypesOverrides
includedTypesOverrides := args.IncludedTypesOverrides
applyGroupOverrides(context.Universe, customArgs)
if err := sanitizePackagePaths(context, args); err != nil {
klog.Fatalf("cannot sanitize inputs: %v", err)
}
applyGroupOverrides(context.Universe, args)
gvToTypes := map[clientgentypes.GroupVersion][]*types.Type{}
groupGoNames := make(map[clientgentypes.GroupVersion]string)
for gv, inputDir := range customArgs.GroupVersionPackages() {
p := context.Universe.Package(path.Vendorless(inputDir))
for gv, inputDir := range args.GroupVersionPackages() {
p := context.Universe.Package(inputDir)
// 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 := types.ExtractCommentTags("+", p.Comments)["groupGoName"]; override != nil {
if override := gengo.ExtractCommentTags("+", p.Comments)["groupGoName"]; override != nil {
groupGoNames[gv] = namer.IC(override[0])
}
// Package are indexed with the vendor prefix stripped
for n, t := range p.Types {
// filter out types which are not included in user specified overrides.
typesOverride, ok := includedTypesOverrides[gv]
@@ -361,33 +394,43 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
}
}
var packageList []generator.Package
clientsetPackage := filepath.Join(arguments.OutputPackagePath, customArgs.ClientsetName)
clientsetDir := filepath.Join(args.OutputDir, args.ClientsetName)
clientsetPkg := path.Join(args.OutputPkg, args.ClientsetName)
packageList = append(packageList, packageForClientset(customArgs, clientsetPackage, groupGoNames, boilerplate))
packageList = append(packageList, packageForScheme(customArgs, clientsetPackage, arguments.OutputBase, groupGoNames, boilerplate))
if customArgs.FakeClient {
packageList = append(packageList, fake.PackageForClientset(customArgs, clientsetPackage, groupGoNames, boilerplate))
var targetList []generator.Target
targetList = append(targetList,
targetForClientset(args, clientsetDir, clientsetPkg, groupGoNames, boilerplate))
targetList = append(targetList,
targetForScheme(args, clientsetDir, clientsetPkg, groupGoNames, boilerplate))
if args.FakeClient {
targetList = append(targetList,
fake.TargetForClientset(args, clientsetDir, clientsetPkg, args.ApplyConfigurationPackage, groupGoNames, boilerplate))
}
// If --clientset-only=true, we don't regenerate the individual typed clients.
if customArgs.ClientsetOnly {
return generator.Packages(packageList)
if args.ClientsetOnly {
return []generator.Target(targetList)
}
orderer := namer.Orderer{Namer: namer.NewPrivateNamer(0)}
gvPackages := customArgs.GroupVersionPackages()
for _, group := range customArgs.Groups {
gvPackages := args.GroupVersionPackages()
for _, group := range args.Groups {
for _, version := range group.Versions {
gv := clientgentypes.GroupVersion{Group: group.Group, Version: version.Version}
types := gvToTypes[gv]
inputPath := gvPackages[gv]
packageList = append(packageList, packageForGroup(gv, orderer.OrderTypes(types), clientsetPackage, group.PackageName, groupGoNames[gv], customArgs.ClientsetAPIPath, arguments.OutputBase, inputPath, customArgs.ApplyConfigurationPackage, boilerplate))
if customArgs.FakeClient {
packageList = append(packageList, fake.PackageForGroup(gv, orderer.OrderTypes(types), clientsetPackage, group.PackageName, groupGoNames[gv], inputPath, customArgs.ApplyConfigurationPackage, boilerplate))
targetList = append(targetList,
targetForGroup(
gv, orderer.OrderTypes(types), clientsetDir, clientsetPkg,
group.PackageName, groupGoNames[gv], args.ClientsetAPIPath,
inputPath, args.ApplyConfigurationPackage, boilerplate))
if args.FakeClient {
targetList = append(targetList,
fake.TargetForGroup(gv, orderer.OrderTypes(types), clientsetDir, clientsetPkg, group.PackageName, groupGoNames[gv], inputPath, args.ApplyConfigurationPackage, boilerplate))
}
}
}
return generator.Packages(packageList)
return targetList
}

View File

@@ -17,45 +17,48 @@ limitations under the License.
package fake
import (
"path"
"path/filepath"
"strings"
"k8s.io/gengo/generator"
"k8s.io/gengo/types"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/types"
clientgenargs "k8s.io/code-generator/cmd/client-gen/args"
"k8s.io/code-generator/cmd/client-gen/args"
scheme "k8s.io/code-generator/cmd/client-gen/generators/scheme"
"k8s.io/code-generator/cmd/client-gen/generators/util"
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
)
func PackageForGroup(gv clientgentypes.GroupVersion, typeList []*types.Type, clientsetPackage string, groupPackageName string, groupGoName string, inputPackage string, applyBuilderPackage string, boilerplate []byte) generator.Package {
outputPackage := filepath.Join(clientsetPackage, "typed", strings.ToLower(groupPackageName), strings.ToLower(gv.Version.NonEmpty()), "fake")
func TargetForGroup(gv clientgentypes.GroupVersion, typeList []*types.Type, clientsetDir, clientsetPkg string, groupPkgName string, groupGoName string, inputPkg string, applyBuilderPackage string, boilerplate []byte) generator.Target {
// TODO: should make this a function, called by here and in client-generator.go
realClientPackage := filepath.Join(clientsetPackage, "typed", strings.ToLower(groupPackageName), strings.ToLower(gv.Version.NonEmpty()))
return &generator.DefaultPackage{
PackageName: "fake",
PackagePath: outputPackage,
HeaderText: boilerplate,
PackageDocumentation: []byte(
`// Package fake has the automatically generated clients.
`),
// GeneratorFunc returns a list of generators. Each generator makes a
subdir := []string{"typed", strings.ToLower(groupPkgName), strings.ToLower(gv.Version.NonEmpty())}
outputDir := filepath.Join(clientsetDir, filepath.Join(subdir...), "fake")
outputPkg := path.Join(clientsetPkg, path.Join(subdir...), "fake")
realClientPkg := path.Join(clientsetPkg, path.Join(subdir...))
return &generator.SimpleTarget{
PkgName: "fake",
PkgPath: outputPkg,
PkgDir: outputDir,
HeaderComment: boilerplate,
PkgDocComment: []byte("// Package fake has the automatically generated clients.\n"),
// GeneratorsFunc returns a list of generators. Each generator makes a
// single file.
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
GeneratorsFunc: func(c *generator.Context) (generators []generator.Generator) {
generators = []generator.Generator{
// Always generate a "doc.go" file.
generator.DefaultGen{OptionalName: "doc"},
generator.GoGenerator{OutputFilename: "doc.go"},
}
// Since we want a file per type that we generate a client for, we
// have to provide a function for this.
for _, t := range typeList {
generators = append(generators, &genFakeForType{
DefaultGen: generator.DefaultGen{
OptionalName: "fake_" + strings.ToLower(c.Namers["private"].Name(t)),
GoGenerator: generator.GoGenerator{
OutputFilename: "fake_" + strings.ToLower(c.Namers["private"].Name(t)) + ".go",
},
outputPackage: outputPackage,
inputPackage: inputPackage,
outputPackage: outputPkg,
inputPackage: inputPkg,
group: gv.Group.NonEmpty(),
version: gv.Version.String(),
groupGoName: groupGoName,
@@ -66,11 +69,11 @@ func PackageForGroup(gv clientgentypes.GroupVersion, typeList []*types.Type, cli
}
generators = append(generators, &genFakeForGroup{
DefaultGen: generator.DefaultGen{
OptionalName: "fake_" + groupPackageName + "_client",
GoGenerator: generator.GoGenerator{
OutputFilename: "fake_" + groupPkgName + "_client.go",
},
outputPackage: outputPackage,
realClientPackage: realClientPackage,
outputPackage: outputPkg,
realClientPackage: realClientPkg,
group: gv.Group.NonEmpty(),
version: gv.Version.String(),
groupGoName: groupGoName,
@@ -85,41 +88,40 @@ func PackageForGroup(gv clientgentypes.GroupVersion, typeList []*types.Type, cli
}
}
func PackageForClientset(customArgs *clientgenargs.CustomArgs, clientsetPackage string, groupGoNames map[clientgentypes.GroupVersion]string, boilerplate []byte) generator.Package {
return &generator.DefaultPackage{
func TargetForClientset(args *args.Args, clientsetDir, clientsetPkg string, applyConfigurationPkg string, groupGoNames map[clientgentypes.GroupVersion]string, boilerplate []byte) generator.Target {
return &generator.SimpleTarget{
// TODO: we'll generate fake clientset for different release in the future.
// Package name and path are hard coded for now.
PackageName: "fake",
PackagePath: filepath.Join(clientsetPackage, "fake"),
HeaderText: boilerplate,
PackageDocumentation: []byte(
`// This package has the automatically generated fake clientset.
`),
// GeneratorFunc returns a list of generators. Each generator generates a
PkgName: "fake",
PkgPath: path.Join(clientsetPkg, "fake"),
PkgDir: filepath.Join(clientsetDir, "fake"),
HeaderComment: boilerplate,
PkgDocComment: []byte("// This package has the automatically generated fake clientset.\n"),
// GeneratorsFunc returns a list of generators. Each generator generates a
// single file.
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
GeneratorsFunc: func(c *generator.Context) (generators []generator.Generator) {
generators = []generator.Generator{
// Always generate a "doc.go" file.
generator.DefaultGen{OptionalName: "doc"},
generator.GoGenerator{OutputFilename: "doc.go"},
&genClientset{
DefaultGen: generator.DefaultGen{
OptionalName: "clientset_generated",
GoGenerator: generator.GoGenerator{
OutputFilename: "clientset_generated.go",
},
groups: customArgs.Groups,
groupGoNames: groupGoNames,
fakeClientsetPackage: clientsetPackage,
outputPackage: "fake",
imports: generator.NewImportTracker(),
realClientsetPackage: clientsetPackage,
groups: args.Groups,
groupGoNames: groupGoNames,
fakeClientsetPackage: clientsetPkg,
imports: generator.NewImportTracker(),
realClientsetPackage: clientsetPkg,
applyConfigurationPackage: applyConfigurationPkg,
},
&scheme.GenScheme{
DefaultGen: generator.DefaultGen{
OptionalName: "register",
GoGenerator: generator.GoGenerator{
OutputFilename: "register.go",
},
InputPackages: customArgs.GroupVersionPackages(),
OutputPackage: clientsetPackage,
Groups: customArgs.Groups,
InputPackages: args.GroupVersionPackages(),
OutputPkg: clientsetPkg,
Groups: args.Groups,
GroupGoNames: groupGoNames,
ImportTracker: generator.NewImportTracker(),
PrivateScheme: true,

View File

@@ -19,33 +19,33 @@ package fake
import (
"fmt"
"io"
"path/filepath"
"path"
"strings"
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
"k8s.io/gengo/v2/types"
)
// genClientset generates a package for a clientset.
type genClientset struct {
generator.DefaultGen
generator.GoGenerator
groups []clientgentypes.GroupVersions
groupGoNames map[clientgentypes.GroupVersion]string
fakeClientsetPackage string
outputPackage string
fakeClientsetPackage string // must be a Go import-path
imports namer.ImportTracker
clientsetGenerated bool
// the import path of the generated real clientset.
realClientsetPackage string
realClientsetPackage string // must be a Go import-path
applyConfigurationPackage string
}
var _ generator.Generator = &genClientset{}
func (g *genClientset) Namers(c *generator.Context) namer.NameSystems {
return namer.NameSystems{
"raw": namer.NewRawNamer(g.outputPackage, g.imports),
"raw": namer.NewRawNamer(g.fakeClientsetPackage, g.imports),
}
}
@@ -60,8 +60,8 @@ func (g *genClientset) Imports(c *generator.Context) (imports []string) {
imports = append(imports, g.imports.ImportLines()...)
for _, group := range g.groups {
for _, version := range group.Versions {
groupClientPackage := filepath.Join(g.fakeClientsetPackage, "typed", strings.ToLower(group.PackageName), strings.ToLower(version.NonEmpty()))
fakeGroupClientPackage := filepath.Join(groupClientPackage, "fake")
groupClientPackage := path.Join(g.fakeClientsetPackage, "typed", strings.ToLower(group.PackageName), strings.ToLower(version.NonEmpty()))
fakeGroupClientPackage := path.Join(groupClientPackage, "fake")
groupAlias := strings.ToLower(g.groupGoNames[clientgentypes.GroupVersion{Group: group.Group, Version: version.Version}])
imports = append(imports, fmt.Sprintf("%s%s \"%s\"", groupAlias, strings.ToLower(version.NonEmpty()), groupClientPackage))
@@ -77,12 +77,15 @@ func (g *genClientset) Imports(c *generator.Context) (imports []string) {
"fakediscovery \"k8s.io/client-go/discovery/fake\"",
"k8s.io/apimachinery/pkg/runtime",
"k8s.io/apimachinery/pkg/watch",
"k8s.io/apimachinery/pkg/api/meta/testrestmapper",
)
return
}
func (g *genClientset) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
generateApply := len(g.applyConfigurationPackage) > 0
// TODO: We actually don't need any type information to generate the clientset,
// perhaps we can adapt the go2ild framework to this kind of usage.
sw := generator.NewSnippetWriter(w, c, "$", "$")
@@ -90,6 +93,13 @@ func (g *genClientset) GenerateType(c *generator.Context, t *types.Type, w io.Wr
allGroups := clientgentypes.ToGroupVersionInfo(g.groups, g.groupGoNames)
sw.Do(common, nil)
if generateApply {
sw.Do(managedFieldsClientset, map[string]any{
"newTypeConverter": types.Ref(g.applyConfigurationPackage, "NewTypeConverter"),
})
}
sw.Do(checkImpl, nil)
for _, group := range allGroups {
@@ -108,11 +118,50 @@ func (g *genClientset) GenerateType(c *generator.Context, t *types.Type, w io.Wr
}
// This part of code is version-independent, unchanging.
var common = `
// NewSimpleClientset returns a clientset that will respond with the provided objects.
var managedFieldsClientset = `
// NewClientset returns a clientset that will respond with the provided objects.
// It's backed by a very simple object tracker that processes creates, updates and deletions as-is,
// without applying any validations and/or defaults. It shouldn't be considered a replacement
// for a real clientset and is mostly useful in simple unit tests.
func NewClientset(objects ...runtime.Object) *Clientset {
o := testing.NewFieldManagedObjectTracker(
scheme,
codecs.UniversalDecoder(),
$.newTypeConverter|raw$(scheme),
)
for _, obj := range objects {
if err := o.Add(obj); err != nil {
panic(err)
}
}
cs := &Clientset{tracker: o}
cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake}
cs.AddReactor("*", "*", testing.ObjectReaction(o))
cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
gvr := action.GetResource()
ns := action.GetNamespace()
watch, err := o.Watch(gvr, ns)
if err != nil {
return false, nil, err
}
return true, watch, nil
})
return cs
}
`
var common = `
// NewSimpleClientset returns a clientset that will respond with the provided objects.
// It's backed by a very simple object tracker that processes creates, updates and deletions as-is,
// without applying any field management, validations and/or defaults. It shouldn't be considered a replacement
// for a real clientset and is mostly useful in simple unit tests.
//
// DEPRECATED: NewClientset replaces this with support for field management, which significantly improves
// server side apply testing. NewClientset is only available when apply configurations are generated (e.g.
// via --with-applyconfig).
func NewSimpleClientset(objects ...runtime.Object) *Clientset {
o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder())
for _, obj := range objects {

View File

@@ -19,21 +19,21 @@ package fake
import (
"fmt"
"io"
"path/filepath"
"path"
"strings"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
"k8s.io/gengo/v2/types"
"k8s.io/code-generator/cmd/client-gen/generators/util"
)
// genFakeForGroup produces a file for a group client, e.g. ExtensionsClient for the extension group.
type genFakeForGroup struct {
generator.DefaultGen
outputPackage string
realClientPackage string
generator.GoGenerator
outputPackage string // must be a Go import-path
realClientPackage string // must be a Go import-path
group string
version string
groupGoName string
@@ -64,7 +64,7 @@ func (g *genFakeForGroup) Namers(c *generator.Context) namer.NameSystems {
func (g *genFakeForGroup) Imports(c *generator.Context) (imports []string) {
imports = g.imports.ImportLines()
if len(g.types) != 0 {
imports = append(imports, fmt.Sprintf("%s \"%s\"", strings.ToLower(filepath.Base(g.realClientPackage)), g.realClientPackage))
imports = append(imports, fmt.Sprintf("%s \"%s\"", strings.ToLower(path.Base(g.realClientPackage)), g.realClientPackage))
}
return imports
}
@@ -90,7 +90,7 @@ func (g *genFakeForGroup) GenerateType(c *generator.Context, t *types.Type, w io
"type": t,
"GroupGoName": g.groupGoName,
"Version": namer.IC(g.version),
"realClientPackage": strings.ToLower(filepath.Base(g.realClientPackage)),
"realClientPackage": strings.ToLower(path.Base(g.realClientPackage)),
}
if tags.NonNamespaced {
sw.Do(getterImplNonNamespaced, wrapper)

View File

@@ -18,21 +18,23 @@ package fake
import (
"io"
gopath "path"
"path/filepath"
"path"
"strings"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
"k8s.io/gengo/v2/types"
"k8s.io/code-generator/cmd/client-gen/generators/util"
)
// genFakeForType produces a file for each top-level type.
type genFakeForType struct {
generator.DefaultGen
outputPackage string
generator.GoGenerator
outputPackage string // Must be a Go import-path
group string
version string
groupGoName string
@@ -44,6 +46,8 @@ type genFakeForType struct {
var _ generator.Generator = &genFakeForType{}
var titler = cases.Title(language.Und)
// Filter ignores all but one type because we're making a single file per type.
func (g *genFakeForType) Filter(c *generator.Context, t *types.Type) bool { return t == g.typeToMatch }
@@ -87,7 +91,7 @@ func hasObjectMeta(t *types.Type) bool {
// GenerateType makes the body of a file implementing the individual typed client for type t.
func (g *genFakeForType) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "$", "$")
pkg := filepath.Base(t.Name.Package)
pkg := path.Base(t.Name.Package)
tags, err := util.ParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
if err != nil {
return err
@@ -120,40 +124,38 @@ func (g *genFakeForType) GenerateType(c *generator.Context, t *types.Type, w io.
"watchInterface": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/watch", Name: "Interface"}),
"jsonMarshal": c.Universe.Type(types.Name{Package: "encoding/json", Name: "Marshal"}),
"NewRootListAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootListAction"}),
"NewListAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewListAction"}),
"NewRootGetAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootGetAction"}),
"NewGetAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewGetAction"}),
"NewRootDeleteAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootDeleteAction"}),
"NewRootDeleteActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootDeleteActionWithOptions"}),
"NewDeleteAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewDeleteAction"}),
"NewDeleteActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewDeleteActionWithOptions"}),
"NewRootDeleteCollectionAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootDeleteCollectionAction"}),
"NewDeleteCollectionAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewDeleteCollectionAction"}),
"NewRootUpdateAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootUpdateAction"}),
"NewUpdateAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewUpdateAction"}),
"NewRootCreateAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootCreateAction"}),
"NewCreateAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewCreateAction"}),
"NewRootWatchAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootWatchAction"}),
"NewWatchAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewWatchAction"}),
"NewCreateSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewCreateSubresourceAction"}),
"NewRootCreateSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootCreateSubresourceAction"}),
"NewUpdateSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewUpdateSubresourceAction"}),
"NewGetSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewGetSubresourceAction"}),
"NewRootGetSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootGetSubresourceAction"}),
"NewRootUpdateSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootUpdateSubresourceAction"}),
"NewRootPatchAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootPatchAction"}),
"NewPatchAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewPatchAction"}),
"NewRootPatchSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootPatchSubresourceAction"}),
"NewPatchSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewPatchSubresourceAction"}),
"ExtractFromListOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "ExtractFromListOptions"}),
"NewRootListActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootListActionWithOptions"}),
"NewListActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewListActionWithOptions"}),
"NewRootGetActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootGetActionWithOptions"}),
"NewGetActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewGetActionWithOptions"}),
"NewRootDeleteActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootDeleteActionWithOptions"}),
"NewDeleteActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewDeleteActionWithOptions"}),
"NewRootDeleteCollectionActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootDeleteCollectionActionWithOptions"}),
"NewDeleteCollectionActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewDeleteCollectionActionWithOptions"}),
"NewRootUpdateActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootUpdateActionWithOptions"}),
"NewUpdateActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewUpdateActionWithOptions"}),
"NewRootCreateActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootCreateActionWithOptions"}),
"NewCreateActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewCreateActionWithOptions"}),
"NewRootWatchActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootWatchActionWithOptions"}),
"NewWatchActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewWatchActionWithOptions"}),
"NewCreateSubresourceActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewCreateSubresourceActionWithOptions"}),
"NewRootCreateSubresourceActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootCreateSubresourceActionWithOptions"}),
"NewUpdateSubresourceActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewUpdateSubresourceActionWithOptions"}),
"NewGetSubresourceActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewGetSubresourceActionWithOptions"}),
"NewRootGetSubresourceActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootGetSubresourceActionWithOptions"}),
"NewRootUpdateSubresourceActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootUpdateSubresourceActionWithOptions"}),
"NewRootPatchActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootPatchActionWithOptions"}),
"NewPatchActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewPatchActionWithOptions"}),
"NewRootPatchSubresourceActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootPatchSubresourceActionWithOptions"}),
"NewPatchSubresourceActionWithOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewPatchSubresourceActionWithOptions"}),
"ExtractFromListOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "ExtractFromListOptions"}),
}
generateApply := len(g.applyConfigurationPackage) > 0
if generateApply {
// Generated apply builder type references required for generated Apply function
_, gvString := util.ParsePathGroupVersion(g.inputPackage)
m["inputApplyConfig"] = types.Ref(gopath.Join(g.applyConfigurationPackage, gvString), t.Name.Name+"ApplyConfiguration")
m["inputApplyConfig"] = types.Ref(path.Join(g.applyConfigurationPackage, gvString), t.Name.Name+"ApplyConfiguration")
}
if tags.NonNamespaced {
@@ -237,7 +239,7 @@ func (g *genFakeForType) GenerateType(c *generator.Context, t *types.Type, w io.
m["resultType"] = &resultType
m["subresourcePath"] = e.SubResourcePath
if e.HasVerb("apply") {
m["inputApplyConfig"] = types.Ref(gopath.Join(g.applyConfigurationPackage, inputGVString), inputType.Name.Name+"ApplyConfiguration")
m["inputApplyConfig"] = types.Ref(path.Join(g.applyConfigurationPackage, inputGVString), inputType.Name.Name+"ApplyConfiguration")
}
if e.HasVerb("get") {
@@ -300,7 +302,7 @@ func (g *genFakeForType) GenerateType(c *generator.Context, t *types.Type, w io.
// TODO: Make the verbs in templates parametrized so the strings.Replace() is
// not needed.
func adjustTemplate(name, verbType, template string) string {
return strings.Replace(template, " "+strings.Title(verbType), " "+name, -1)
return strings.ReplaceAll(template, " "+titler.String(verbType), " "+name)
}
// template for the struct that implements the type's interface
@@ -331,11 +333,12 @@ var $.type|allLowercasePlural$Kind = $.SchemeGroupVersion|raw$.WithKind("$.type|
var listTemplate = `
// List takes label and field selectors, and returns the list of $.type|publicPlural$ that match those selectors.
func (c *Fake$.type|publicPlural$) List(ctx context.Context, opts $.ListOptions|raw$) (result *$.type|raw$List, err error) {
emptyResult := &$.type|raw$List{}
obj, err := c.Fake.
$if .namespaced$Invokes($.NewListAction|raw$($.type|allLowercasePlural$Resource, $.type|allLowercasePlural$Kind, c.ns, opts), &$.type|raw$List{})
$else$Invokes($.NewRootListAction|raw$($.type|allLowercasePlural$Resource, $.type|allLowercasePlural$Kind, opts), &$.type|raw$List{})$end$
$if .namespaced$Invokes($.NewListActionWithOptions|raw$($.type|allLowercasePlural$Resource, $.type|allLowercasePlural$Kind, c.ns, opts), emptyResult)
$else$Invokes($.NewRootListActionWithOptions|raw$($.type|allLowercasePlural$Resource, $.type|allLowercasePlural$Kind, opts), emptyResult)$end$
if obj == nil {
return nil, err
return emptyResult, err
}
return obj.(*$.type|raw$List), err
}
@@ -344,11 +347,12 @@ func (c *Fake$.type|publicPlural$) List(ctx context.Context, opts $.ListOptions|
var listUsingOptionsTemplate = `
// List takes label and field selectors, and returns the list of $.type|publicPlural$ that match those selectors.
func (c *Fake$.type|publicPlural$) List(ctx context.Context, opts $.ListOptions|raw$) (result *$.type|raw$List, err error) {
emptyResult := &$.type|raw$List{}
obj, err := c.Fake.
$if .namespaced$Invokes($.NewListAction|raw$($.type|allLowercasePlural$Resource, $.type|allLowercasePlural$Kind, c.ns, opts), &$.type|raw$List{})
$else$Invokes($.NewRootListAction|raw$($.type|allLowercasePlural$Resource, $.type|allLowercasePlural$Kind, opts), &$.type|raw$List{})$end$
$if .namespaced$Invokes($.NewListActionWithOptions|raw$($.type|allLowercasePlural$Resource, $.type|allLowercasePlural$Kind, c.ns, opts), emptyResult)
$else$Invokes($.NewRootListActionWithOptions|raw$($.type|allLowercasePlural$Resource, $.type|allLowercasePlural$Kind, opts), emptyResult)$end$
if obj == nil {
return nil, err
return emptyResult, err
}
label, _, _ := $.ExtractFromListOptions|raw$(opts)
@@ -368,11 +372,12 @@ func (c *Fake$.type|publicPlural$) List(ctx context.Context, opts $.ListOptions|
var getTemplate = `
// Get takes name of the $.type|private$, and returns the corresponding $.resultType|private$ object, and an error if there is any.
func (c *Fake$.type|publicPlural$) Get(ctx context.Context, name string, options $.GetOptions|raw$) (result *$.resultType|raw$, err error) {
emptyResult := &$.resultType|raw${}
obj, err := c.Fake.
$if .namespaced$Invokes($.NewGetAction|raw$($.type|allLowercasePlural$Resource, c.ns, name), &$.resultType|raw${})
$else$Invokes($.NewRootGetAction|raw$($.type|allLowercasePlural$Resource, name), &$.resultType|raw${})$end$
$if .namespaced$Invokes($.NewGetActionWithOptions|raw$($.type|allLowercasePlural$Resource, c.ns, name, options), emptyResult)
$else$Invokes($.NewRootGetActionWithOptions|raw$($.type|allLowercasePlural$Resource, name, options), emptyResult)$end$
if obj == nil {
return nil, err
return emptyResult, err
}
return obj.(*$.resultType|raw$), err
}
@@ -381,11 +386,12 @@ func (c *Fake$.type|publicPlural$) Get(ctx context.Context, name string, options
var getSubresourceTemplate = `
// Get takes name of the $.type|private$, and returns the corresponding $.resultType|private$ object, and an error if there is any.
func (c *Fake$.type|publicPlural$) Get(ctx context.Context, $.type|private$Name string, options $.GetOptions|raw$) (result *$.resultType|raw$, err error) {
emptyResult := &$.resultType|raw${}
obj, err := c.Fake.
$if .namespaced$Invokes($.NewGetSubresourceAction|raw$($.type|allLowercasePlural$Resource, c.ns, "$.subresourcePath$", $.type|private$Name), &$.resultType|raw${})
$else$Invokes($.NewRootGetSubresourceAction|raw$($.type|allLowercasePlural$Resource, "$.subresourcePath$", $.type|private$Name), &$.resultType|raw${})$end$
$if .namespaced$Invokes($.NewGetSubresourceActionWithOptions|raw$($.type|allLowercasePlural$Resource, c.ns, "$.subresourcePath$", $.type|private$Name, options), emptyResult)
$else$Invokes($.NewRootGetSubresourceActionWithOptions|raw$($.type|allLowercasePlural$Resource, "$.subresourcePath$", $.type|private$Name, options), emptyResult)$end$
if obj == nil {
return nil, err
return emptyResult, err
}
return obj.(*$.resultType|raw$), err
}
@@ -404,8 +410,8 @@ func (c *Fake$.type|publicPlural$) Delete(ctx context.Context, name string, opts
var deleteCollectionTemplate = `
// DeleteCollection deletes a collection of objects.
func (c *Fake$.type|publicPlural$) DeleteCollection(ctx context.Context, opts $.DeleteOptions|raw$, listOpts $.ListOptions|raw$) error {
$if .namespaced$action := $.NewDeleteCollectionAction|raw$($.type|allLowercasePlural$Resource, c.ns, listOpts)
$else$action := $.NewRootDeleteCollectionAction|raw$($.type|allLowercasePlural$Resource, listOpts)
$if .namespaced$action := $.NewDeleteCollectionActionWithOptions|raw$($.type|allLowercasePlural$Resource, c.ns, opts, listOpts)
$else$action := $.NewRootDeleteCollectionActionWithOptions|raw$($.type|allLowercasePlural$Resource, opts, listOpts)
$end$
_, err := c.Fake.Invokes(action, &$.type|raw$List{})
return err
@@ -414,11 +420,12 @@ func (c *Fake$.type|publicPlural$) DeleteCollection(ctx context.Context, opts $.
var createTemplate = `
// Create takes the representation of a $.inputType|private$ and creates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any.
func (c *Fake$.type|publicPlural$) Create(ctx context.Context, $.inputType|private$ *$.inputType|raw$, opts $.CreateOptions|raw$) (result *$.resultType|raw$, err error) {
emptyResult := &$.resultType|raw${}
obj, err := c.Fake.
$if .namespaced$Invokes($.NewCreateAction|raw$($.inputType|allLowercasePlural$Resource, c.ns, $.inputType|private$), &$.resultType|raw${})
$else$Invokes($.NewRootCreateAction|raw$($.inputType|allLowercasePlural$Resource, $.inputType|private$), &$.resultType|raw${})$end$
$if .namespaced$Invokes($.NewCreateActionWithOptions|raw$($.inputType|allLowercasePlural$Resource, c.ns, $.inputType|private$, opts), emptyResult)
$else$Invokes($.NewRootCreateActionWithOptions|raw$($.inputType|allLowercasePlural$Resource, $.inputType|private$, opts), emptyResult)$end$
if obj == nil {
return nil, err
return emptyResult, err
}
return obj.(*$.resultType|raw$), err
}
@@ -427,11 +434,12 @@ func (c *Fake$.type|publicPlural$) Create(ctx context.Context, $.inputType|priva
var createSubresourceTemplate = `
// Create takes the representation of a $.inputType|private$ and creates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any.
func (c *Fake$.type|publicPlural$) Create(ctx context.Context, $.type|private$Name string, $.inputType|private$ *$.inputType|raw$, opts $.CreateOptions|raw$) (result *$.resultType|raw$, err error) {
emptyResult := &$.resultType|raw${}
obj, err := c.Fake.
$if .namespaced$Invokes($.NewCreateSubresourceAction|raw$($.type|allLowercasePlural$Resource, $.type|private$Name, "$.subresourcePath$", c.ns, $.inputType|private$), &$.resultType|raw${})
$else$Invokes($.NewRootCreateSubresourceAction|raw$($.type|allLowercasePlural$Resource, $.type|private$Name, "$.subresourcePath$", $.inputType|private$), &$.resultType|raw${})$end$
$if .namespaced$Invokes($.NewCreateSubresourceActionWithOptions|raw$($.type|allLowercasePlural$Resource, $.type|private$Name, "$.subresourcePath$", c.ns, $.inputType|private$, opts), emptyResult)
$else$Invokes($.NewRootCreateSubresourceActionWithOptions|raw$($.type|allLowercasePlural$Resource, $.type|private$Name, "$.subresourcePath$", $.inputType|private$, opts), emptyResult)$end$
if obj == nil {
return nil, err
return emptyResult, err
}
return obj.(*$.resultType|raw$), err
}
@@ -440,11 +448,12 @@ func (c *Fake$.type|publicPlural$) Create(ctx context.Context, $.type|private$Na
var updateTemplate = `
// Update takes the representation of a $.inputType|private$ and updates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any.
func (c *Fake$.type|publicPlural$) Update(ctx context.Context, $.inputType|private$ *$.inputType|raw$, opts $.UpdateOptions|raw$) (result *$.resultType|raw$, err error) {
emptyResult := &$.resultType|raw${}
obj, err := c.Fake.
$if .namespaced$Invokes($.NewUpdateAction|raw$($.inputType|allLowercasePlural$Resource, c.ns, $.inputType|private$), &$.resultType|raw${})
$else$Invokes($.NewRootUpdateAction|raw$($.inputType|allLowercasePlural$Resource, $.inputType|private$), &$.resultType|raw${})$end$
$if .namespaced$Invokes($.NewUpdateActionWithOptions|raw$($.inputType|allLowercasePlural$Resource, c.ns, $.inputType|private$, opts), emptyResult)
$else$Invokes($.NewRootUpdateActionWithOptions|raw$($.inputType|allLowercasePlural$Resource, $.inputType|private$, opts), emptyResult)$end$
if obj == nil {
return nil, err
return emptyResult, err
}
return obj.(*$.resultType|raw$), err
}
@@ -453,11 +462,12 @@ func (c *Fake$.type|publicPlural$) Update(ctx context.Context, $.inputType|priva
var updateSubresourceTemplate = `
// Update takes the representation of a $.inputType|private$ and updates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any.
func (c *Fake$.type|publicPlural$) Update(ctx context.Context, $.type|private$Name string, $.inputType|private$ *$.inputType|raw$, opts $.UpdateOptions|raw$) (result *$.resultType|raw$, err error) {
emptyResult := &$.resultType|raw${}
obj, err := c.Fake.
$if .namespaced$Invokes($.NewUpdateSubresourceAction|raw$($.type|allLowercasePlural$Resource, "$.subresourcePath$", c.ns, $.inputType|private$), &$.inputType|raw${})
$else$Invokes($.NewRootUpdateSubresourceAction|raw$($.type|allLowercasePlural$Resource, "$.subresourcePath$", $.inputType|private$), &$.resultType|raw${})$end$
$if .namespaced$Invokes($.NewUpdateSubresourceActionWithOptions|raw$($.type|allLowercasePlural$Resource, "$.subresourcePath$", c.ns, $.inputType|private$, opts), &$.inputType|raw${})
$else$Invokes($.NewRootUpdateSubresourceActionWithOptions|raw$($.type|allLowercasePlural$Resource, "$.subresourcePath$", $.inputType|private$, opts), emptyResult)$end$
if obj == nil {
return nil, err
return emptyResult, err
}
return obj.(*$.resultType|raw$), err
}
@@ -466,12 +476,13 @@ func (c *Fake$.type|publicPlural$) Update(ctx context.Context, $.type|private$Na
var updateStatusTemplate = `
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *Fake$.type|publicPlural$) UpdateStatus(ctx context.Context, $.type|private$ *$.type|raw$, opts $.UpdateOptions|raw$) (*$.type|raw$, error) {
func (c *Fake$.type|publicPlural$) UpdateStatus(ctx context.Context, $.type|private$ *$.type|raw$, opts $.UpdateOptions|raw$) (result *$.type|raw$, err error) {
emptyResult := &$.type|raw${}
obj, err := c.Fake.
$if .namespaced$Invokes($.NewUpdateSubresourceAction|raw$($.type|allLowercasePlural$Resource, "status", c.ns, $.type|private$), &$.type|raw${})
$else$Invokes($.NewRootUpdateSubresourceAction|raw$($.type|allLowercasePlural$Resource, "status", $.type|private$), &$.type|raw${})$end$
$if .namespaced$Invokes($.NewUpdateSubresourceActionWithOptions|raw$($.type|allLowercasePlural$Resource, "status", c.ns, $.type|private$, opts), emptyResult)
$else$Invokes($.NewRootUpdateSubresourceActionWithOptions|raw$($.type|allLowercasePlural$Resource, "status", $.type|private$, opts), emptyResult)$end$
if obj == nil {
return nil, err
return emptyResult, err
}
return obj.(*$.type|raw$), err
}
@@ -481,19 +492,20 @@ var watchTemplate = `
// Watch returns a $.watchInterface|raw$ that watches the requested $.type|privatePlural$.
func (c *Fake$.type|publicPlural$) Watch(ctx context.Context, opts $.ListOptions|raw$) ($.watchInterface|raw$, error) {
return c.Fake.
$if .namespaced$InvokesWatch($.NewWatchAction|raw$($.type|allLowercasePlural$Resource, c.ns, opts))
$else$InvokesWatch($.NewRootWatchAction|raw$($.type|allLowercasePlural$Resource, opts))$end$
$if .namespaced$InvokesWatch($.NewWatchActionWithOptions|raw$($.type|allLowercasePlural$Resource, c.ns, opts))
$else$InvokesWatch($.NewRootWatchActionWithOptions|raw$($.type|allLowercasePlural$Resource, opts))$end$
}
`
var patchTemplate = `
// Patch applies the patch and returns the patched $.resultType|private$.
func (c *Fake$.type|publicPlural$) Patch(ctx context.Context, name string, pt $.PatchType|raw$, data []byte, opts $.PatchOptions|raw$, subresources ...string) (result *$.resultType|raw$, err error) {
emptyResult := &$.resultType|raw${}
obj, err := c.Fake.
$if .namespaced$Invokes($.NewPatchSubresourceAction|raw$($.type|allLowercasePlural$Resource, c.ns, name, pt, data, subresources... ), &$.resultType|raw${})
$else$Invokes($.NewRootPatchSubresourceAction|raw$($.type|allLowercasePlural$Resource, name, pt, data, subresources...), &$.resultType|raw${})$end$
$if .namespaced$Invokes($.NewPatchSubresourceActionWithOptions|raw$($.type|allLowercasePlural$Resource, c.ns, name, pt, data, opts, subresources... ), emptyResult)
$else$Invokes($.NewRootPatchSubresourceActionWithOptions|raw$($.type|allLowercasePlural$Resource, name, pt, data, opts, subresources...), emptyResult)$end$
if obj == nil {
return nil, err
return emptyResult, err
}
return obj.(*$.resultType|raw$), err
}
@@ -509,15 +521,16 @@ func (c *Fake$.type|publicPlural$) Apply(ctx context.Context, $.inputType|privat
if err != nil {
return nil, err
}
name := $.inputType|private$.Name
name := $.inputType|private$.Name
if name == nil {
return nil, fmt.Errorf("$.inputType|private$.Name must be provided to Apply")
}
emptyResult := &$.resultType|raw${}
obj, err := c.Fake.
$if .namespaced$Invokes($.NewPatchSubresourceAction|raw$($.type|allLowercasePlural$Resource, c.ns, *name, $.ApplyPatchType|raw$, data), &$.resultType|raw${})
$else$Invokes($.NewRootPatchSubresourceAction|raw$($.type|allLowercasePlural$Resource, *name, $.ApplyPatchType|raw$, data), &$.resultType|raw${})$end$
$if .namespaced$Invokes($.NewPatchSubresourceActionWithOptions|raw$($.type|allLowercasePlural$Resource, c.ns, *name, $.ApplyPatchType|raw$, data, opts.ToPatchOptions()), emptyResult)
$else$Invokes($.NewRootPatchSubresourceActionWithOptions|raw$($.type|allLowercasePlural$Resource, *name, $.ApplyPatchType|raw$, data, opts.ToPatchOptions()), emptyResult)$end$
if obj == nil {
return nil, err
return emptyResult, err
}
return obj.(*$.resultType|raw$), err
}
@@ -534,15 +547,16 @@ func (c *Fake$.type|publicPlural$) ApplyStatus(ctx context.Context, $.inputType|
if err != nil {
return nil, err
}
name := $.inputType|private$.Name
name := $.inputType|private$.Name
if name == nil {
return nil, fmt.Errorf("$.inputType|private$.Name must be provided to Apply")
}
emptyResult := &$.resultType|raw${}
obj, err := c.Fake.
$if .namespaced$Invokes($.NewPatchSubresourceAction|raw$($.type|allLowercasePlural$Resource, c.ns, *name, $.ApplyPatchType|raw$, data, "status"), &$.resultType|raw${})
$else$Invokes($.NewRootPatchSubresourceAction|raw$($.type|allLowercasePlural$Resource, *name, $.ApplyPatchType|raw$, data, "status"), &$.resultType|raw${})$end$
$if .namespaced$Invokes($.NewPatchSubresourceActionWithOptions|raw$($.type|allLowercasePlural$Resource, c.ns, *name, $.ApplyPatchType|raw$, data, opts.ToPatchOptions(), "status"), emptyResult)
$else$Invokes($.NewRootPatchSubresourceActionWithOptions|raw$($.type|allLowercasePlural$Resource, *name, $.ApplyPatchType|raw$, data, opts.ToPatchOptions(), "status"), emptyResult)$end$
if obj == nil {
return nil, err
return emptyResult, err
}
return obj.(*$.resultType|raw$), err
}
@@ -559,11 +573,12 @@ func (c *Fake$.type|publicPlural$) Apply(ctx context.Context, $.type|private$Nam
if err != nil {
return nil, err
}
emptyResult := &$.resultType|raw${}
obj, err := c.Fake.
$if .namespaced$Invokes($.NewPatchSubresourceAction|raw$($.type|allLowercasePlural$Resource, c.ns, $.type|private$Name, $.ApplyPatchType|raw$, data, "status"), &$.resultType|raw${})
$else$Invokes($.NewRootPatchSubresourceAction|raw$($.type|allLowercasePlural$Resource, $.type|private$Name, $.ApplyPatchType|raw$, data, "status"), &$.resultType|raw${})$end$
$if .namespaced$Invokes($.NewPatchSubresourceActionWithOptions|raw$($.type|allLowercasePlural$Resource, c.ns, $.type|private$Name, $.ApplyPatchType|raw$, data, opts.ToPatchOptions(), "$.inputType|private$"), emptyResult)
$else$Invokes($.NewRootPatchSubresourceActionWithOptions|raw$($.type|allLowercasePlural$Resource, $.type|private$Name, $.ApplyPatchType|raw$, data, opts.ToPatchOptions(), "$.inputType|private$"), emptyResult)$end$
if obj == nil {
return nil, err
return emptyResult, err
}
return obj.(*$.resultType|raw$), err
}

View File

@@ -19,22 +19,21 @@ package generators
import (
"fmt"
"io"
"path/filepath"
"path"
"strings"
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
"k8s.io/gengo/v2/types"
)
// genClientset generates a package for a clientset.
type genClientset struct {
generator.DefaultGen
generator.GoGenerator
groups []clientgentypes.GroupVersions
groupGoNames map[clientgentypes.GroupVersion]string
clientsetPackage string
outputPackage string
clientsetPackage string // must be a Go import-path
imports namer.ImportTracker
clientsetGenerated bool
}
@@ -43,7 +42,7 @@ var _ generator.Generator = &genClientset{}
func (g *genClientset) Namers(c *generator.Context) namer.NameSystems {
return namer.NameSystems{
"raw": namer.NewRawNamer(g.outputPackage, g.imports),
"raw": namer.NewRawNamer(g.clientsetPackage, g.imports),
}
}
@@ -58,7 +57,7 @@ func (g *genClientset) Imports(c *generator.Context) (imports []string) {
imports = append(imports, g.imports.ImportLines()...)
for _, group := range g.groups {
for _, version := range group.Versions {
typedClientPath := filepath.Join(g.clientsetPackage, "typed", strings.ToLower(group.PackageName), strings.ToLower(version.NonEmpty()))
typedClientPath := path.Join(g.clientsetPackage, "typed", strings.ToLower(group.PackageName), strings.ToLower(version.NonEmpty()))
groupAlias := strings.ToLower(g.groupGoNames[clientgentypes.GroupVersion{Group: group.Group, Version: version.Version}])
imports = append(imports, fmt.Sprintf("%s%s \"%s\"", groupAlias, strings.ToLower(version.NonEmpty()), typedClientPath))
}

View File

@@ -22,13 +22,13 @@ import (
"path/filepath"
"strings"
"k8s.io/gengo/generator"
"k8s.io/gengo/types"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/types"
)
// genExpansion produces a file for a group client, e.g. ExtensionsClient for the extension group.
type genExpansion struct {
generator.DefaultGen
generator.GoGenerator
groupPackagePath string
// types in a group
types []*types.Type

View File

@@ -18,19 +18,19 @@ package generators
import (
"io"
"path/filepath"
"path"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/gengo/v2"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
"k8s.io/gengo/v2/types"
"k8s.io/code-generator/cmd/client-gen/generators/util"
"k8s.io/code-generator/cmd/client-gen/path"
)
// genGroup produces a file for a group client, e.g. ExtensionsClient for the extension group.
type genGroup struct {
generator.DefaultGen
generator.GoGenerator
outputPackage string
group string
version string
@@ -40,7 +40,7 @@ type genGroup struct {
types []*types.Type
imports namer.ImportTracker
inputPackage string
clientsetPackage string
clientsetPackage string // must be a Go import-path
// If the genGroup has been called. This generator should only execute once.
called bool
}
@@ -64,38 +64,32 @@ func (g *genGroup) Namers(c *generator.Context) namer.NameSystems {
func (g *genGroup) Imports(c *generator.Context) (imports []string) {
imports = append(imports, g.imports.ImportLines()...)
imports = append(imports, filepath.Join(g.clientsetPackage, "scheme"))
imports = append(imports, path.Join(g.clientsetPackage, "scheme"))
return
}
func (g *genGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "$", "$")
apiPath := func(group string) string {
if group == "core" {
return `"/api"`
}
return `"` + g.apiPath + `"`
}
groupName := g.group
if g.group == "core" {
groupName = ""
}
// allow user to define a group name that's different from the one parsed from the directory.
p := c.Universe.Package(path.Vendorless(g.inputPackage))
if override := types.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil {
p := c.Universe.Package(g.inputPackage)
groupName := g.group
if override := gengo.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil {
groupName = override[0]
}
apiPath := `"` + g.apiPath + `"`
if groupName == "" {
apiPath = `"/api"`
}
m := map[string]interface{}{
"group": g.group,
"version": g.version,
"groupName": groupName,
"GroupGoName": g.groupGoName,
"Version": namer.IC(g.version),
"types": g.types,
"apiPath": apiPath(g.group),
"apiPath": apiPath,
"schemaGroupVersion": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/runtime/schema", Name: "GroupVersion"}),
"runtimeAPIVersionInternal": c.Universe.Variable(types.Name{Package: "k8s.io/apimachinery/pkg/runtime", Name: "APIVersionInternal"}),
"restConfig": c.Universe.Type(types.Name{Package: "k8s.io/client-go/rest", Name: "Config"}),
@@ -104,7 +98,7 @@ func (g *genGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer
"RESTHTTPClientFor": c.Universe.Function(types.Name{Package: "k8s.io/client-go/rest", Name: "HTTPClientFor"}),
"restRESTClientFor": c.Universe.Function(types.Name{Package: "k8s.io/client-go/rest", Name: "RESTClientFor"}),
"restRESTClientForConfigAndClient": c.Universe.Function(types.Name{Package: "k8s.io/client-go/rest", Name: "RESTClientForConfigAndClient"}),
"SchemeGroupVersion": c.Universe.Variable(types.Name{Package: path.Vendorless(g.inputPackage), Name: "SchemeGroupVersion"}),
"SchemeGroupVersion": c.Universe.Variable(types.Name{Package: g.inputPackage, Name: "SchemeGroupVersion"}),
}
sw.Do(groupInterfaceTemplate, m)
sw.Do(groupClientTemplate, m)

View File

@@ -19,23 +19,24 @@ package generators
import (
"io"
"path"
"path/filepath"
"strings"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
"k8s.io/gengo/v2/types"
"k8s.io/code-generator/cmd/client-gen/generators/util"
)
// genClientForType produces a file for each top-level type.
type genClientForType struct {
generator.DefaultGen
outputPackage string
generator.GoGenerator
outputPackage string // must be a Go import-path
inputPackage string
clientsetPackage string
applyConfigurationPackage string
clientsetPackage string // must be a Go import-path
applyConfigurationPackage string // must be a Go import-path
group string
version string
groupGoName string
@@ -45,6 +46,8 @@ type genClientForType struct {
var _ generator.Generator = &genClientForType{}
var titler = cases.Title(language.Und)
// Filter ignores all but one type because we're making a single file per type.
func (g *genClientForType) Filter(c *generator.Context, t *types.Type) bool {
return t == g.typeToMatch
@@ -81,7 +84,7 @@ func (g *genClientForType) GenerateType(c *generator.Context, t *types.Type, w i
defaultVerbTemplates := buildDefaultVerbTemplates(generateApply)
subresourceDefaultVerbTemplates := buildSubresourceDefaultVerbTemplates(generateApply)
sw := generator.NewSnippetWriter(w, c, "$", "$")
pkg := filepath.Base(t.Name.Package)
pkg := path.Base(t.Name.Package)
tags, err := util.ParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
if err != nil {
return err
@@ -120,9 +123,9 @@ func (g *genClientForType) GenerateType(c *generator.Context, t *types.Type, w i
}
var updatedVerbtemplate string
if _, exists := subresourceDefaultVerbTemplates[e.VerbType]; e.IsSubresource() && exists {
updatedVerbtemplate = e.VerbName + "(" + strings.TrimPrefix(subresourceDefaultVerbTemplates[e.VerbType], strings.Title(e.VerbType)+"(")
updatedVerbtemplate = e.VerbName + "(" + strings.TrimPrefix(subresourceDefaultVerbTemplates[e.VerbType], titler.String(e.VerbType)+"(")
} else {
updatedVerbtemplate = e.VerbName + "(" + strings.TrimPrefix(defaultVerbTemplates[e.VerbType], strings.Title(e.VerbType)+"(")
updatedVerbtemplate = e.VerbName + "(" + strings.TrimPrefix(defaultVerbTemplates[e.VerbType], titler.String(e.VerbType)+"(")
}
extendedMethod := extendedInterfaceMethod{
template: updatedVerbtemplate,
@@ -145,30 +148,38 @@ 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,
"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"}),
"ApplyPatchType": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/types", Name: "ApplyPatchType"}),
"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: filepath.Join(g.clientsetPackage, "scheme"), Name: "ParameterCodec"}),
"jsonMarshal": c.Universe.Type(types.Name{Package: "encoding/json", Name: "Marshal"}),
"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,
"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"}),
"ApplyPatchType": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/types", Name: "ApplyPatchType"}),
"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"}),
"jsonMarshal": c.Universe.Type(types.Name{Package: "encoding/json", Name: "Marshal"}),
"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"}),
"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"}),
}
if generateApply {
@@ -203,53 +214,31 @@ func (g *genClientForType) GenerateType(c *generator.Context, t *types.Type, w i
}
sw.Do(interfaceTemplate4, m)
structNamespaced := namespaced
if tags.NonNamespaced {
sw.Do(structNonNamespaced, m)
sw.Do(newStructNonNamespaced, m)
} else {
sw.Do(structNamespaced, m)
sw.Do(newStructNamespaced, m)
structNamespaced = nonNamespaced
}
if tags.NoVerbs {
sw.Do(structType[noList|noApply], m)
sw.Do(newStruct[structNamespaced|noList|noApply], m)
return sw.Error()
}
if tags.HasVerb("get") {
sw.Do(getTemplate, m)
}
listableOrAppliable := noList | noApply
if tags.HasVerb("list") {
sw.Do(listTemplate, m)
}
if tags.HasVerb("watch") {
sw.Do(watchTemplate, m)
listableOrAppliable |= withList
}
if tags.HasVerb("create") {
sw.Do(createTemplate, m)
}
if tags.HasVerb("update") {
sw.Do(updateTemplate, m)
}
if tags.HasVerb("updateStatus") {
sw.Do(updateStatusTemplate, m)
}
if tags.HasVerb("delete") {
sw.Do(deleteTemplate, m)
}
if tags.HasVerb("deleteCollection") {
sw.Do(deleteCollectionTemplate, m)
}
if tags.HasVerb("patch") {
sw.Do(patchTemplate, m)
}
if tags.HasVerb("apply") && generateApply {
sw.Do(applyTemplate, m)
}
if tags.HasVerb("applyStatus") && generateApply {
sw.Do(applyStatusTemplate, m)
listableOrAppliable |= withApply
}
sw.Do(structType[listableOrAppliable], m)
sw.Do(newStruct[structNamespaced|listableOrAppliable], m)
// generate expansion methods
for _, e := range tags.Extensions {
if e.HasVerb("apply") && !generateApply {
@@ -295,6 +284,8 @@ func (g *genClientForType) GenerateType(c *generator.Context, t *types.Type, w i
sw.Do(adjustTemplate(e.VerbName, e.VerbType, listSubresourceTemplate), m)
} else {
sw.Do(adjustTemplate(e.VerbName, e.VerbType, listTemplate), m)
sw.Do(adjustTemplate(e.VerbName, e.VerbType, privateListTemplate), m)
sw.Do(adjustTemplate(e.VerbName, e.VerbType, watchListTemplate), m)
}
}
@@ -345,7 +336,7 @@ func (g *genClientForType) GenerateType(c *generator.Context, t *types.Type, w i
// TODO: Make the verbs in templates parametrized so the strings.Replace() is
// not needed.
func adjustTemplate(name, verbType, template string) string {
return strings.Replace(template, " "+strings.Title(verbType), " "+name, -1)
return strings.ReplaceAll(template, " "+titler.String(verbType), " "+name)
}
func generateInterface(defaultVerbTemplates map[string]string, tags util.Tags) string {
@@ -374,9 +365,10 @@ func buildSubresourceDefaultVerbTemplates(generateApply bool) map[string]string
func buildDefaultVerbTemplates(generateApply bool) map[string]string {
m := map[string]string{
"create": `Create(ctx context.Context, $.inputType|private$ *$.inputType|raw$, opts $.CreateOptions|raw$) (*$.resultType|raw$, error)`,
"update": `Update(ctx context.Context, $.inputType|private$ *$.inputType|raw$, opts $.UpdateOptions|raw$) (*$.resultType|raw$, error)`,
"updateStatus": `UpdateStatus(ctx context.Context, $.inputType|private$ *$.type|raw$, opts $.UpdateOptions|raw$) (*$.type|raw$, error)`,
"create": `Create(ctx context.Context, $.inputType|private$ *$.inputType|raw$, opts $.CreateOptions|raw$) (*$.resultType|raw$, error)`,
"update": `Update(ctx context.Context, $.inputType|private$ *$.inputType|raw$, opts $.UpdateOptions|raw$) (*$.resultType|raw$, error)`,
"updateStatus": `// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
UpdateStatus(ctx context.Context, $.inputType|private$ *$.type|raw$, opts $.UpdateOptions|raw$) (*$.type|raw$, error)`,
"delete": `Delete(ctx context.Context, name string, opts $.DeleteOptions|raw$) error`,
"deleteCollection": `DeleteCollection(ctx context.Context, opts $.DeleteOptions|raw$, listOpts $.ListOptions|raw$) error`,
"get": `Get(ctx context.Context, name string, opts $.GetOptions|raw$) (*$.resultType|raw$, error)`,
@@ -386,7 +378,8 @@ func buildDefaultVerbTemplates(generateApply bool) map[string]string {
}
if generateApply {
m["apply"] = `Apply(ctx context.Context, $.inputType|private$ *$.inputApplyConfig|raw$, opts $.ApplyOptions|raw$) (result *$.resultType|raw$, err error)`
m["applyStatus"] = `ApplyStatus(ctx context.Context, $.inputType|private$ *$.inputApplyConfig|raw$, opts $.ApplyOptions|raw$) (result *$.resultType|raw$, err error)`
m["applyStatus"] = `// Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus().
ApplyStatus(ctx context.Context, $.inputType|private$ *$.inputApplyConfig|raw$, opts $.ApplyOptions|raw$) (result *$.resultType|raw$, err error)`
}
return m
}
@@ -418,51 +411,204 @@ var interfaceTemplate4 = `
}
`
// template for the struct that implements the type's interface
var structNamespaced = `
// $.type|privatePlural$ implements $.type|public$Interface
type $.type|privatePlural$ struct {
client $.RESTClientInterface|raw$
ns string
}
`
// struct and constructor variants
const (
// The following values are bits in a bitmask.
// The values which can be set indicate namespace support, list support, and apply support;
// to make the declarations easier to read (like a truth table), corresponding zero-values
// are also declared.
namespaced = 0
noList = 0
noApply = 0
nonNamespaced = 1 << iota
withList
withApply
)
// template for the struct that implements the type's interface
var structNonNamespaced = `
// $.type|privatePlural$ implements $.type|public$Interface
type $.type|privatePlural$ struct {
client $.RESTClientInterface|raw$
}
`
// The following string slices are similar to maps, but with combinable keys used as indices.
// Each entry defines whether it supports lists and/or apply, and if namespacedness matters,
// namespaces; each bit is then toggled:
// * noList, noApply: index 0;
// * withList, noApply: index 2;
// * noList, withApply: index 4;
// * withList, withApply: index 6.
// When namespacedness matters, the namespaced variants are the same as the above, and
// the non-namespaced variants are offset by 1.
// Go enforces index unicity in these kinds of declarations.
var newStructNamespaced = `
// new$.type|publicPlural$ returns a $.type|publicPlural$
func new$.type|publicPlural$(c *$.GroupGoName$$.Version$Client, namespace string) *$.type|privatePlural$ {
return &$.type|privatePlural${
client: c.RESTClient(),
ns: namespace,
// struct declarations
// Namespacedness does not matter
var structType = []string{
noList | noApply: `
// $.type|privatePlural$ implements $.type|public$Interface
type $.type|privatePlural$ struct {
*$.Client|raw$[*$.resultType|raw$]
}
}
`
var newStructNonNamespaced = `
// new$.type|publicPlural$ returns a $.type|publicPlural$
func new$.type|publicPlural$(c *$.GroupGoName$$.Version$Client) *$.type|privatePlural$ {
return &$.type|privatePlural${
client: c.RESTClient(),
`,
withList | noApply: `
// $.type|privatePlural$ implements $.type|public$Interface
type $.type|privatePlural$ struct {
*$.ClientWithList|raw$[*$.resultType|raw$, *$.resultType|raw$List]
}
`,
noList | withApply: `
// $.type|privatePlural$ implements $.type|public$Interface
type $.type|privatePlural$ struct {
*$.ClientWithApply|raw$[*$.resultType|raw$, *$.inputApplyConfig|raw$]
}
`,
withList | withApply: `
// $.type|privatePlural$ implements $.type|public$Interface
type $.type|privatePlural$ struct {
*$.ClientWithListAndApply|raw$[*$.resultType|raw$, *$.resultType|raw$List, *$.inputApplyConfig|raw$]
}
`,
}
`
// Constructors for the struct, in all variants
// Namespacedness matters
var newStruct = []string{
namespaced | noList | noApply: `
// new$.type|publicPlural$ returns a $.type|publicPlural$
func new$.type|publicPlural$(c *$.GroupGoName$$.Version$Client, namespace string) *$.type|privatePlural$ {
return &$.type|privatePlural${
gentype.NewClient[*$.resultType|raw$](
"$.type|resource$",
c.RESTClient(),
$.schemeParameterCodec|raw$,
namespace,
func() *$.resultType|raw$ { return &$.resultType|raw${} }),
}
}
`,
namespaced | noList | withApply: `
// new$.type|publicPlural$ returns a $.type|publicPlural$
func new$.type|publicPlural$(c *$.GroupGoName$$.Version$Client, namespace string) *$.type|privatePlural$ {
return &$.type|privatePlural${
gentype.NewClientWithApply[*$.resultType|raw$, *$.inputApplyConfig|raw$](
"$.type|resource$",
c.RESTClient(),
$.schemeParameterCodec|raw$,
namespace,
func() *$.resultType|raw$ { return &$.resultType|raw${} }),
}
}
`,
namespaced | withList | noApply: `
// new$.type|publicPlural$ returns a $.type|publicPlural$
func new$.type|publicPlural$(c *$.GroupGoName$$.Version$Client, namespace string) *$.type|privatePlural$ {
return &$.type|privatePlural${
gentype.NewClientWithList[*$.resultType|raw$, *$.resultType|raw$List](
"$.type|resource$",
c.RESTClient(),
$.schemeParameterCodec|raw$,
namespace,
func() *$.resultType|raw$ { return &$.resultType|raw${} },
func() *$.resultType|raw$List { return &$.resultType|raw$List{} }),
}
}
`,
namespaced | withList | withApply: `
// new$.type|publicPlural$ returns a $.type|publicPlural$
func new$.type|publicPlural$(c *$.GroupGoName$$.Version$Client, namespace string) *$.type|privatePlural$ {
return &$.type|privatePlural${
gentype.NewClientWithListAndApply[*$.resultType|raw$, *$.resultType|raw$List, *$.inputApplyConfig|raw$](
"$.type|resource$",
c.RESTClient(),
$.schemeParameterCodec|raw$,
namespace,
func() *$.resultType|raw$ { return &$.resultType|raw${} },
func() *$.resultType|raw$List { return &$.resultType|raw$List{} }),
}
}
`,
nonNamespaced | noList | noApply: `
// new$.type|publicPlural$ returns a $.type|publicPlural$
func new$.type|publicPlural$(c *$.GroupGoName$$.Version$Client) *$.type|privatePlural$ {
return &$.type|privatePlural${
gentype.NewClient[*$.resultType|raw$](
"$.type|resource$",
c.RESTClient(),
$.schemeParameterCodec|raw$,
"",
func() *$.resultType|raw$ { return &$.resultType|raw${} }),
}
}
`,
nonNamespaced | noList | withApply: `
// new$.type|publicPlural$ returns a $.type|publicPlural$
func new$.type|publicPlural$(c *$.GroupGoName$$.Version$Client) *$.type|privatePlural$ {
return &$.type|privatePlural${
gentype.NewClientWithApply[*$.resultType|raw$, *$.inputApplyConfig|raw$](
"$.type|resource$",
c.RESTClient(),
$.schemeParameterCodec|raw$,
"",
func() *$.resultType|raw$ { return &$.resultType|raw${} }),
}
}
`,
nonNamespaced | withList | noApply: `
// new$.type|publicPlural$ returns a $.type|publicPlural$
func new$.type|publicPlural$(c *$.GroupGoName$$.Version$Client) *$.type|privatePlural$ {
return &$.type|privatePlural${
gentype.NewClientWithList[*$.resultType|raw$, *$.resultType|raw$List](
"$.type|resource$",
c.RESTClient(),
$.schemeParameterCodec|raw$,
"",
func() *$.resultType|raw$ { return &$.resultType|raw${} },
func() *$.resultType|raw$List { return &$.resultType|raw$List{} }),
}
}
`,
nonNamespaced | withList | withApply: `
// new$.type|publicPlural$ returns a $.type|publicPlural$
func new$.type|publicPlural$(c *$.GroupGoName$$.Version$Client) *$.type|privatePlural$ {
return &$.type|privatePlural${
gentype.NewClientWithListAndApply[*$.resultType|raw$, *$.resultType|raw$List, *$.inputApplyConfig|raw$](
"$.type|resource$",
c.RESTClient(),
$.schemeParameterCodec|raw$,
"",
func() *$.resultType|raw$ { return &$.resultType|raw${} },
func() *$.resultType|raw$List { return &$.resultType|raw$List{} }),
}
}
`,
}
var listTemplate = `
// List takes label and field selectors, and returns the list of $.resultType|publicPlural$ that match those selectors.
func (c *$.type|privatePlural$) List(ctx context.Context, opts $.ListOptions|raw$) (result *$.resultType|raw$List, err error) {
func (c *$.type|privatePlural$) List(ctx context.Context, opts $.ListOptions|raw$) (*$.resultType|raw$List, error) {
if watchListOptions, hasWatchListOptionsPrepared, watchListOptionsErr := $.PrepareWatchListOptionsFromListOptions|raw$(opts); watchListOptionsErr != nil {
klog.Warningf("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
}
klog.Warningf("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.Context, opts $.ListOptions|raw$) (result *$.resultType|raw$List, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil{
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &$.resultType|raw$List{}
err = c.client.Get().
$if .namespaced$Namespace(c.ns).$end$
err = c.GetClient().Get().
$if .namespaced$Namespace(c.GetNamespace()).$end$
Resource("$.type|resource$").
VersionedParams(&opts, $.schemeParameterCodec|raw$).
Timeout(timeout).
@@ -480,8 +626,8 @@ func (c *$.type|privatePlural$) List(ctx context.Context, $.type|private$Name st
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &$.resultType|raw$List{}
err = c.client.Get().
$if .namespaced$Namespace(c.ns).$end$
err = c.GetClient().Get().
$if .namespaced$Namespace(c.GetNamespace()).$end$
Resource("$.type|resource$").
Name($.type|private$Name).
SubResource("$.subresourcePath$").
@@ -497,8 +643,8 @@ var getTemplate = `
// Get takes name of the $.type|private$, and returns the corresponding $.resultType|private$ object, and an error if there is any.
func (c *$.type|privatePlural$) Get(ctx context.Context, name string, options $.GetOptions|raw$) (result *$.resultType|raw$, err error) {
result = &$.resultType|raw${}
err = c.client.Get().
$if .namespaced$Namespace(c.ns).$end$
err = c.GetClient().Get().
$if .namespaced$Namespace(c.GetNamespace()).$end$
Resource("$.type|resource$").
Name(name).
VersionedParams(&options, $.schemeParameterCodec|raw$).
@@ -512,8 +658,8 @@ var getSubresourceTemplate = `
// Get takes name of the $.type|private$, and returns the corresponding $.resultType|raw$ object, and an error if there is any.
func (c *$.type|privatePlural$) Get(ctx context.Context, $.type|private$Name string, options $.GetOptions|raw$) (result *$.resultType|raw$, err error) {
result = &$.resultType|raw${}
err = c.client.Get().
$if .namespaced$Namespace(c.ns).$end$
err = c.GetClient().Get().
$if .namespaced$Namespace(c.GetNamespace()).$end$
Resource("$.type|resource$").
Name($.type|private$Name).
SubResource("$.subresourcePath$").
@@ -527,8 +673,8 @@ func (c *$.type|privatePlural$) Get(ctx context.Context, $.type|private$Name str
var deleteTemplate = `
// Delete takes name of the $.type|private$ and deletes it. Returns an error if one occurs.
func (c *$.type|privatePlural$) Delete(ctx context.Context, name string, opts $.DeleteOptions|raw$) error {
return c.client.Delete().
$if .namespaced$Namespace(c.ns).$end$
return c.GetClient().Delete().
$if .namespaced$Namespace(c.GetNamespace()).$end$
Resource("$.type|resource$").
Name(name).
Body(&opts).
@@ -537,30 +683,12 @@ func (c *$.type|privatePlural$) Delete(ctx context.Context, name string, opts $.
}
`
var deleteCollectionTemplate = `
// DeleteCollection deletes a collection of objects.
func (c *$.type|privatePlural$) DeleteCollection(ctx context.Context, opts $.DeleteOptions|raw$, listOpts $.ListOptions|raw$) error {
var timeout time.Duration
if listOpts.TimeoutSeconds != nil{
timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
}
return c.client.Delete().
$if .namespaced$Namespace(c.ns).$end$
Resource("$.type|resource$").
VersionedParams(&listOpts, $.schemeParameterCodec|raw$).
Timeout(timeout).
Body(&opts).
Do(ctx).
Error()
}
`
var createSubresourceTemplate = `
// Create takes the representation of a $.inputType|private$ and creates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any.
func (c *$.type|privatePlural$) Create(ctx context.Context, $.type|private$Name string, $.inputType|private$ *$.inputType|raw$, opts $.CreateOptions|raw$) (result *$.resultType|raw$, err error) {
result = &$.resultType|raw${}
err = c.client.Post().
$if .namespaced$Namespace(c.ns).$end$
err = c.GetClient().Post().
$if .namespaced$Namespace(c.GetNamespace()).$end$
Resource("$.type|resource$").
Name($.type|private$Name).
SubResource("$.subresourcePath$").
@@ -576,8 +704,8 @@ var createTemplate = `
// Create takes the representation of a $.inputType|private$ and creates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any.
func (c *$.type|privatePlural$) Create(ctx context.Context, $.inputType|private$ *$.inputType|raw$, opts $.CreateOptions|raw$) (result *$.resultType|raw$, err error) {
result = &$.resultType|raw${}
err = c.client.Post().
$if .namespaced$Namespace(c.ns).$end$
err = c.GetClient().Post().
$if .namespaced$Namespace(c.GetNamespace()).$end$
Resource("$.type|resource$").
VersionedParams(&opts, $.schemeParameterCodec|raw$).
Body($.inputType|private$).
@@ -591,8 +719,8 @@ var updateSubresourceTemplate = `
// Update takes the top resource name and the representation of a $.inputType|private$ and updates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any.
func (c *$.type|privatePlural$) Update(ctx context.Context, $.type|private$Name string, $.inputType|private$ *$.inputType|raw$, opts $.UpdateOptions|raw$) (result *$.resultType|raw$, err error) {
result = &$.resultType|raw${}
err = c.client.Put().
$if .namespaced$Namespace(c.ns).$end$
err = c.GetClient().Put().
$if .namespaced$Namespace(c.GetNamespace()).$end$
Resource("$.type|resource$").
Name($.type|private$Name).
SubResource("$.subresourcePath$").
@@ -608,8 +736,8 @@ var updateTemplate = `
// Update takes the representation of a $.inputType|private$ and updates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any.
func (c *$.type|privatePlural$) Update(ctx context.Context, $.inputType|private$ *$.inputType|raw$, opts $.UpdateOptions|raw$) (result *$.resultType|raw$, err error) {
result = &$.resultType|raw${}
err = c.client.Put().
$if .namespaced$Namespace(c.ns).$end$
err = c.GetClient().Put().
$if .namespaced$Namespace(c.GetNamespace()).$end$
Resource("$.type|resource$").
Name($.inputType|private$.Name).
VersionedParams(&opts, $.schemeParameterCodec|raw$).
@@ -620,24 +748,6 @@ func (c *$.type|privatePlural$) Update(ctx context.Context, $.inputType|private$
}
`
var updateStatusTemplate = `
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *$.type|privatePlural$) UpdateStatus(ctx context.Context, $.type|private$ *$.type|raw$, opts $.UpdateOptions|raw$) (result *$.type|raw$, err error) {
result = &$.type|raw${}
err = c.client.Put().
$if .namespaced$Namespace(c.ns).$end$
Resource("$.type|resource$").
Name($.type|private$.Name).
SubResource("status").
VersionedParams(&opts, $.schemeParameterCodec|raw$).
Body($.type|private$).
Do(ctx).
Into(result)
return
}
`
var watchTemplate = `
// Watch returns a $.watchInterface|raw$ that watches the requested $.type|privatePlural$.
func (c *$.type|privatePlural$) Watch(ctx context.Context, opts $.ListOptions|raw$) ($.watchInterface|raw$, error) {
@@ -646,12 +756,30 @@ func (c *$.type|privatePlural$) Watch(ctx context.Context, opts $.ListOptions|ra
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
opts.Watch = true
return c.client.Get().
return c.GetClient().Get().
$if .namespaced$Namespace(c.GetNamespace()).$end$
VersionedParams(&opts, $.schemeParameterCodec|raw$).
Timeout(timeout).
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.Context, opts $.ListOptions|raw$) (result *$.resultType|raw$List, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil{
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &$.resultType|raw$List{}
err = c.client.Get().
$if .namespaced$Namespace(c.ns).$end$
Resource("$.type|resource$").
VersionedParams(&opts, $.schemeParameterCodec|raw$).
Timeout(timeout).
Watch(ctx)
WatchList(ctx).
Into(result)
return
}
`
@@ -659,8 +787,8 @@ var patchTemplate = `
// Patch applies the patch and returns the patched $.resultType|private$.
func (c *$.type|privatePlural$) Patch(ctx context.Context, name string, pt $.PatchType|raw$, data []byte, opts $.PatchOptions|raw$, subresources ...string) (result *$.resultType|raw$, err error) {
result = &$.resultType|raw${}
err = c.client.Patch(pt).
$if .namespaced$Namespace(c.ns).$end$
err = c.GetClient().Patch(pt).
$if .namespaced$Namespace(c.GetNamespace()).$end$
Resource("$.type|resource$").
Name(name).
SubResource(subresources...).
@@ -688,8 +816,8 @@ func (c *$.type|privatePlural$) Apply(ctx context.Context, $.inputType|private$
return nil, fmt.Errorf("$.inputType|private$.Name must be provided to Apply")
}
result = &$.resultType|raw${}
err = c.client.Patch($.ApplyPatchType|raw$).
$if .namespaced$Namespace(c.ns).$end$
err = c.GetClient().Patch($.ApplyPatchType|raw$).
$if .namespaced$Namespace(c.GetNamespace()).$end$
Resource("$.type|resource$").
Name(*name).
VersionedParams(&patchOpts, $.schemeParameterCodec|raw$).
@@ -700,38 +828,6 @@ func (c *$.type|privatePlural$) Apply(ctx context.Context, $.inputType|private$
}
`
var applyStatusTemplate = `
// ApplyStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus().
func (c *$.type|privatePlural$) ApplyStatus(ctx context.Context, $.inputType|private$ *$.inputApplyConfig|raw$, opts $.ApplyOptions|raw$) (result *$.resultType|raw$, err error) {
if $.inputType|private$ == nil {
return nil, fmt.Errorf("$.inputType|private$ provided to Apply must not be nil")
}
patchOpts := opts.ToPatchOptions()
data, err := $.jsonMarshal|raw$($.inputType|private$)
if err != nil {
return nil, err
}
name := $.inputType|private$.Name
if name == nil {
return nil, fmt.Errorf("$.inputType|private$.Name must be provided to Apply")
}
result = &$.resultType|raw${}
err = c.client.Patch($.ApplyPatchType|raw$).
$if .namespaced$Namespace(c.ns).$end$
Resource("$.type|resource$").
Name(*name).
SubResource("status").
VersionedParams(&patchOpts, $.schemeParameterCodec|raw$).
Body(data).
Do(ctx).
Into(result)
return
}
`
var applySubresourceTemplate = `
// Apply takes top resource name and the apply declarative configuration for $.subresourcePath$,
// applies it and returns the applied $.resultType|private$, and an error, if there is any.
@@ -746,8 +842,8 @@ func (c *$.type|privatePlural$) Apply(ctx context.Context, $.type|private$Name s
}
result = &$.resultType|raw${}
err = c.client.Patch($.ApplyPatchType|raw$).
$if .namespaced$Namespace(c.ns).$end$
err = c.GetClient().Patch($.ApplyPatchType|raw$).
$if .namespaced$Namespace(c.GetNamespace()).$end$
Resource("$.type|resource$").
Name($.type|private$Name).
SubResource("$.subresourcePath$").

View File

@@ -20,24 +20,24 @@ import (
"fmt"
"io"
"os"
"path"
"path/filepath"
"strings"
"k8s.io/code-generator/cmd/client-gen/path"
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
"k8s.io/gengo/v2/types"
)
// GenScheme produces a package for a clientset with the scheme, codecs and parameter codecs.
type GenScheme struct {
generator.DefaultGen
OutputPackage string
generator.GoGenerator
OutputPkg string // Must be a Go import-path
OutputPath string // optional
Groups []clientgentypes.GroupVersions
GroupGoNames map[clientgentypes.GroupVersion]string
InputPackages map[clientgentypes.GroupVersion]string
OutputPath string
ImportTracker namer.ImportTracker
PrivateScheme bool
CreateRegistry bool
@@ -46,7 +46,7 @@ type GenScheme struct {
func (g *GenScheme) Namers(c *generator.Context) namer.NameSystems {
return namer.NameSystems{
"raw": namer.NewRawNamer(g.OutputPackage, g.ImportTracker),
"raw": namer.NewRawNamer(g.OutputPkg, g.ImportTracker),
}
}
@@ -66,14 +66,14 @@ func (g *GenScheme) Imports(c *generator.Context) (imports []string) {
if g.CreateRegistry {
// import the install package for internal clientsets instead of the type package with register.go
if version.Version != "" {
packagePath = filepath.Dir(packagePath)
packagePath = path.Dir(packagePath)
}
packagePath = filepath.Join(packagePath, "install")
packagePath = path.Join(packagePath, "install")
imports = append(imports, fmt.Sprintf("%s \"%s\"", groupAlias, path.Vendorless(packagePath)))
imports = append(imports, fmt.Sprintf("%s \"%s\"", groupAlias, packagePath))
break
} else {
imports = append(imports, fmt.Sprintf("%s%s \"%s\"", groupAlias, strings.ToLower(version.Version.NonEmpty()), path.Vendorless(packagePath)))
imports = append(imports, fmt.Sprintf("%s%s \"%s\"", groupAlias, strings.ToLower(version.Version.NonEmpty()), packagePath))
}
}
}

View File

@@ -21,7 +21,7 @@ import (
"fmt"
"strings"
"k8s.io/gengo/types"
"k8s.io/gengo/v2"
)
var supportedTags = []string{
@@ -192,7 +192,7 @@ func MustParseClientGenTags(lines []string) Tags {
// tags are provided.
func ParseClientGenTags(lines []string) (Tags, error) {
ret := Tags{}
values := types.ExtractCommentTags("+", lines)
values := gengo.ExtractCommentTags("+", lines)
var value []string
value, ret.GenerateClient = values["genclient"]
// Check the old format and error when used to avoid generating client when //+genclient=false

View File

@@ -23,40 +23,44 @@ import (
"github.com/spf13/pflag"
"k8s.io/klog/v2"
generatorargs "k8s.io/code-generator/cmd/client-gen/args"
"k8s.io/code-generator/cmd/client-gen/args"
"k8s.io/code-generator/cmd/client-gen/generators"
"k8s.io/code-generator/pkg/util"
"k8s.io/gengo/v2"
"k8s.io/gengo/v2/generator"
)
func main() {
klog.InitFlags(nil)
genericArgs, customArgs := generatorargs.NewDefaults()
args := args.New()
// Override defaults.
// TODO: move this out of client-gen
genericArgs.OutputPackagePath = "k8s.io/kubernetes/pkg/client/clientset_generated/"
genericArgs.AddFlags(pflag.CommandLine)
customArgs.AddFlags(pflag.CommandLine, "k8s.io/kubernetes/pkg/apis") // TODO: move this input path out of client-gen
args.AddFlags(pflag.CommandLine, "k8s.io/kubernetes/pkg/apis") // TODO: move this input path out of client-gen
flag.Set("logtostderr", "true")
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
pflag.Parse()
// add group version package as input dirs for gengo
for _, pkg := range customArgs.Groups {
inputPkgs := []string{}
for _, pkg := range args.Groups {
for _, v := range pkg.Versions {
genericArgs.InputDirs = append(genericArgs.InputDirs, v.Package)
inputPkgs = append(inputPkgs, v.Package)
}
}
if err := generatorargs.Validate(genericArgs); err != nil {
if err := args.Validate(); err != nil {
klog.Fatalf("Error: %v", err)
}
if err := genericArgs.Execute(
generators.NameSystems(util.PluralExceptionListToMapOrDie(customArgs.PluralExceptions)),
myTargets := func(context *generator.Context) []generator.Target {
return generators.GetTargets(context, args)
}
if err := gengo.Execute(
generators.NameSystems(util.PluralExceptionListToMapOrDie(args.PluralExceptions)),
generators.DefaultNameSystem(),
generators.Packages,
myTargets,
gengo.StdBuildTag,
inputPkgs,
); err != nil {
klog.Fatalf("Error: %v", err)
}

View File

@@ -1,31 +0,0 @@
/*
Copyright 2017 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 path
import "strings"
// Vendorless removes the longest match of "*/vendor/" from the front of p.
// It is useful if a package locates in vendor/, e.g.,
// k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/apis/meta/v1, because gengo
// indexes the package with its import path, e.g.,
// k8s.io/apimachinery/pkg/apis/meta/v1,
func Vendorless(p string) string {
if pos := strings.LastIndex(p, "/vendor/"); pos != -1 {
return p[pos+len("/vendor/"):]
}
return p
}

View File

@@ -22,7 +22,7 @@ import (
"sort"
"strings"
"k8s.io/gengo/namer"
"k8s.io/gengo/v2/namer"
)
// ToGroupVersion turns "group/version" string into a GroupVersion struct. It reports error
@@ -116,6 +116,6 @@ func ToGroupInstallPackages(groups []GroupVersions, groupGoNames map[GroupVersio
}
// NormalizeGroupVersion calls normalizes the GroupVersion.
//func NormalizeGroupVersion(gv GroupVersion) GroupVersion {
// return GroupVersion{Group: gv.Group.NonEmpty(), Version: gv.Version, NonEmptyVersion: normalization.Version(gv.Version)}
//}
// func NormalizeGroupVersion(gv GroupVersion) GroupVersion {
// return GroupVersion{Group: gv.Group.NonEmpty(), Version: gv.Version, NonEmptyVersion: normalization.Version(gv.Version)}
// }

View File

@@ -42,7 +42,7 @@ func (g Group) String() string {
}
func (g Group) NonEmpty() string {
if g == "api" {
if g == "" {
return "core"
}
return string(g)
@@ -56,6 +56,8 @@ func (g Group) PackageName() string {
return strings.ToLower(parts[0])
}
type Kind string
type PackageVersion struct {
Version
// The fully qualified package, e.g. k8s.io/kubernetes/pkg/apis/apps, where the types.go is found.
@@ -67,14 +69,24 @@ type GroupVersion struct {
Version Version
}
type GroupVersionKind struct {
Group Group
Version Version
Kind Kind
}
func (gv GroupVersion) ToAPIVersion() string {
if len(gv.Group) > 0 && gv.Group.NonEmpty() != "core" {
if len(gv.Group) > 0 && gv.Group != "" {
return gv.Group.String() + "/" + gv.Version.String()
} else {
return gv.Version.String()
}
}
func (gv GroupVersion) WithKind(kind Kind) GroupVersionKind {
return GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: kind}
}
type GroupVersions struct {
// The name of the package for this group, e.g. apps.
PackageName string

View File

@@ -20,35 +20,33 @@ import (
"fmt"
"github.com/spf13/pflag"
"k8s.io/gengo/args"
"k8s.io/gengo/examples/deepcopy-gen/generators"
)
// CustomArgs is used by the gengo framework to pass args specific to this generator.
type CustomArgs generators.CustomArgs
type Args struct {
OutputFile string
BoundingDirs []string // Only deal with types rooted under these dirs.
GoHeaderFile string
}
// NewDefaults returns default arguments for the generator.
func NewDefaults() (*args.GeneratorArgs, *CustomArgs) {
genericArgs := args.Default().WithoutDefaultFlagParsing()
customArgs := &CustomArgs{}
genericArgs.CustomArgs = (*generators.CustomArgs)(customArgs) // convert to upstream type to make type-casts work there
genericArgs.OutputFileBaseName = "deepcopy_generated"
return genericArgs, customArgs
// New returns default arguments for the generator.
func New() *Args {
return &Args{}
}
// AddFlags add the generator flags to the flag set.
func (ca *CustomArgs) AddFlags(fs *pflag.FlagSet) {
pflag.CommandLine.StringSliceVar(&ca.BoundingDirs, "bounding-dirs", ca.BoundingDirs,
func (args *Args) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&args.OutputFile, "output-file", "generated.deepcopy.go",
"the name of the file to be generated")
fs.StringSliceVar(&args.BoundingDirs, "bounding-dirs", args.BoundingDirs,
"Comma-separated list of import paths which bound the types for which deep-copies will be generated.")
fs.StringVar(&args.GoHeaderFile, "go-header-file", "",
"the path to a file containing boilerplate header text; the string \"YEAR\" will be replaced with the current 4-digit year")
}
// Validate checks the given arguments.
func Validate(genericArgs *args.GeneratorArgs) error {
_ = genericArgs.CustomArgs.(*generators.CustomArgs)
if len(genericArgs.OutputFileBaseName) == 0 {
return fmt.Errorf("output file base name cannot be empty")
func (args *Args) Validate() error {
if len(args.OutputFile) == 0 {
return fmt.Errorf("--output-file must be specified")
}
return nil
}

View File

@@ -0,0 +1,892 @@
/*
Copyright 2015 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 generators
import (
"fmt"
"io"
"path"
"sort"
"strings"
"k8s.io/code-generator/cmd/deepcopy-gen/args"
"k8s.io/gengo/v2"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
"k8s.io/gengo/v2/types"
"k8s.io/klog/v2"
)
// This is the comment tag that carries parameters for deep-copy generation.
const (
tagEnabledName = "k8s:deepcopy-gen"
interfacesTagName = tagEnabledName + ":interfaces"
interfacesNonPointerTagName = tagEnabledName + ":nonpointer-interfaces" // attach the DeepCopy<Interface> methods to the
)
// Known values for the comment tag.
const tagValuePackage = "package"
// enabledTagValue holds parameters from a tagName tag.
type enabledTagValue struct {
value string
register bool
}
func extractEnabledTypeTag(t *types.Type) *enabledTagValue {
comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...)
return extractEnabledTag(comments)
}
func extractEnabledTag(comments []string) *enabledTagValue {
tagVals := gengo.ExtractCommentTags("+", comments)[tagEnabledName]
if tagVals == 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 we got here we are returning something.
tag := &enabledTagValue{}
// Get the primary value.
parts := strings.Split(tagVals[0], ",")
if len(parts) >= 1 {
tag.value = parts[0]
}
// Parse extra arguments.
parts = parts[1:]
for i := range parts {
kv := strings.SplitN(parts[i], "=", 2)
k := kv[0]
v := ""
if len(kv) == 2 {
v = kv[1]
}
switch k {
case "register":
if v != "false" {
tag.register = true
}
default:
klog.Fatalf("Unsupported %s param: %q", tagEnabledName, parts[i])
}
}
return tag
}
// TODO: This is created only to reduce number of changes in a single PR.
// Remove it and use PublicNamer instead.
func deepCopyNamer() *namer.NameStrategy {
return &namer.NameStrategy{
Join: func(pre string, in []string, post string) string {
return strings.Join(in, "_")
},
PrependPackageNames: 1,
}
}
// NameSystems returns the name system used by the generators in this package.
func NameSystems() namer.NameSystems {
return namer.NameSystems{
"public": deepCopyNamer(),
"raw": namer.NewRawNamer("", nil),
}
}
// DefaultNameSystem returns the default name system for ordering the types to be
// processed by the generators in this package.
func DefaultNameSystem() string {
return "public"
}
func GetTargets(context *generator.Context, args *args.Args) []generator.Target {
boilerplate, err := gengo.GoBoilerplate(args.GoHeaderFile, gengo.StdBuildTag, gengo.StdGeneratedBy)
if err != nil {
klog.Fatalf("Failed loading boilerplate: %v", err)
}
boundingDirs := []string{}
if args.BoundingDirs == nil {
args.BoundingDirs = context.Inputs
}
for i := range args.BoundingDirs {
// Strip any trailing slashes - they are not exactly "correct" but
// this is friendlier.
boundingDirs = append(boundingDirs, strings.TrimRight(args.BoundingDirs[i], "/"))
}
targets := []generator.Target{}
for _, i := range context.Inputs {
klog.V(3).Infof("Considering pkg %q", i)
pkg := context.Universe[i]
ptag := extractEnabledTag(pkg.Comments)
ptagValue := ""
ptagRegister := false
if ptag != nil {
ptagValue = ptag.value
if ptagValue != tagValuePackage {
klog.Fatalf("Package %v: unsupported %s value: %q", i, tagEnabledName, ptagValue)
}
ptagRegister = ptag.register
klog.V(3).Infof(" tag.value: %q, tag.register: %t", ptagValue, ptagRegister)
} else {
klog.V(3).Infof(" no tag")
}
// If the pkg-scoped tag says to generate, we can skip scanning types.
pkgNeedsGeneration := (ptagValue == tagValuePackage)
if !pkgNeedsGeneration {
// If the pkg-scoped tag did not exist, scan all types for one that
// explicitly wants generation. Ensure all types that want generation
// can be copied.
var uncopyable []string
for _, t := range pkg.Types {
klog.V(3).Infof(" considering type %q", t.Name.String())
ttag := extractEnabledTypeTag(t)
if ttag != nil && ttag.value == "true" {
klog.V(3).Infof(" tag=true")
if !copyableType(t) {
uncopyable = append(uncopyable, fmt.Sprintf("%v", t))
} else {
pkgNeedsGeneration = true
}
}
}
if len(uncopyable) > 0 {
klog.Fatalf("Types requested deepcopy generation but are not copyable: %s",
strings.Join(uncopyable, ", "))
}
}
if pkgNeedsGeneration {
klog.V(3).Infof("Package %q needs generation", i)
targets = append(targets,
&generator.SimpleTarget{
PkgName: strings.Split(path.Base(pkg.Path), ".")[0],
PkgPath: pkg.Path,
PkgDir: pkg.Dir, // output pkg is the same as the input
HeaderComment: boilerplate,
FilterFunc: func(c *generator.Context, t *types.Type) bool {
return t.Name.Package == pkg.Path
},
GeneratorsFunc: func(c *generator.Context) (generators []generator.Generator) {
return []generator.Generator{
NewGenDeepCopy(args.OutputFile, pkg.Path, boundingDirs, (ptagValue == tagValuePackage), ptagRegister),
}
},
})
}
}
return targets
}
// genDeepCopy produces a file with autogenerated deep-copy functions.
type genDeepCopy struct {
generator.GoGenerator
targetPackage string
boundingDirs []string
allTypes bool
registerTypes bool
imports namer.ImportTracker
typesForInit []*types.Type
}
func NewGenDeepCopy(outputFilename, targetPackage string, boundingDirs []string, allTypes, registerTypes bool) generator.Generator {
return &genDeepCopy{
GoGenerator: generator.GoGenerator{
OutputFilename: outputFilename,
},
targetPackage: targetPackage,
boundingDirs: boundingDirs,
allTypes: allTypes,
registerTypes: registerTypes,
imports: generator.NewImportTracker(),
typesForInit: make([]*types.Type, 0),
}
}
func (g *genDeepCopy) Namers(c *generator.Context) namer.NameSystems {
// Have the raw namer for this file track what it imports.
return namer.NameSystems{
"raw": namer.NewRawNamer(g.targetPackage, g.imports),
}
}
func (g *genDeepCopy) Filter(c *generator.Context, t *types.Type) bool {
// Filter out types not being processed or not copyable within the package.
enabled := g.allTypes
if !enabled {
ttag := extractEnabledTypeTag(t)
if ttag != nil && ttag.value == "true" {
enabled = true
}
}
if !enabled {
return false
}
if !copyableType(t) {
klog.V(3).Infof("Type %v is not copyable", t)
return false
}
klog.V(3).Infof("Type %v is copyable", t)
g.typesForInit = append(g.typesForInit, t)
return true
}
// deepCopyMethod returns the signature of a DeepCopy() method, nil or an error
// if the type does not match. This allows more efficient deep copy
// implementations to be defined by the type's author. The correct signature
// for a type T is:
//
// func (t T) DeepCopy() T
//
// or:
//
// func (t *T) DeepCopy() *T
func deepCopyMethod(t *types.Type) (*types.Signature, error) {
f, found := t.Methods["DeepCopy"]
if !found {
return nil, nil
}
if len(f.Signature.Parameters) != 0 {
return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected no parameters", t)
}
if len(f.Signature.Results) != 1 {
return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected exactly one result", t)
}
ptrResult := f.Signature.Results[0].Kind == types.Pointer && f.Signature.Results[0].Elem.Name == t.Name
nonPtrResult := f.Signature.Results[0].Name == t.Name
if !ptrResult && !nonPtrResult {
return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected to return %s or *%s", t, t.Name.Name, t.Name.Name)
}
ptrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Kind == types.Pointer && f.Signature.Receiver.Elem.Name == t.Name
nonPtrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Name == t.Name
if ptrRcvr && !ptrResult {
return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected a *%s result for a *%s receiver", t, t.Name.Name, t.Name.Name)
}
if nonPtrRcvr && !nonPtrResult {
return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected a %s result for a %s receiver", t, t.Name.Name, t.Name.Name)
}
return f.Signature, nil
}
// deepCopyMethodOrDie returns the signatrue of a DeepCopy method, nil or calls klog.Fatalf
// if the type does not match.
func deepCopyMethodOrDie(t *types.Type) *types.Signature {
ret, err := deepCopyMethod(t)
if err != nil {
klog.Fatal(err)
}
return ret
}
// deepCopyIntoMethod returns the signature of a DeepCopyInto() method, nil or an error
// if the type is wrong. DeepCopyInto allows more efficient deep copy
// implementations to be defined by the type's author. The correct signature
// for a type T is:
//
// func (t T) DeepCopyInto(t *T)
//
// or:
//
// func (t *T) DeepCopyInto(t *T)
func deepCopyIntoMethod(t *types.Type) (*types.Signature, error) {
f, found := t.Methods["DeepCopyInto"]
if !found {
return nil, nil
}
if len(f.Signature.Parameters) != 1 {
return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected exactly one parameter", t)
}
if len(f.Signature.Results) != 0 {
return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected no result type", t)
}
ptrParam := f.Signature.Parameters[0].Kind == types.Pointer && f.Signature.Parameters[0].Elem.Name == t.Name
if !ptrParam {
return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected parameter of type *%s", t, t.Name.Name)
}
ptrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Kind == types.Pointer && f.Signature.Receiver.Elem.Name == t.Name
nonPtrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Name == t.Name
if !ptrRcvr && !nonPtrRcvr {
// this should never happen
return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected a receiver of type %s or *%s", t, t.Name.Name, t.Name.Name)
}
return f.Signature, nil
}
// deepCopyIntoMethodOrDie returns the signature of a DeepCopyInto() method, nil or calls klog.Fatalf
// if the type is wrong.
func deepCopyIntoMethodOrDie(t *types.Type) *types.Signature {
ret, err := deepCopyIntoMethod(t)
if err != nil {
klog.Fatal(err)
}
return ret
}
func copyableType(t *types.Type) bool {
// If the type opts out of copy-generation, stop.
ttag := extractEnabledTypeTag(t)
if ttag != nil && ttag.value == "false" {
return false
}
// Filter out private types.
if namer.IsPrivateGoName(t.Name.Name) {
return false
}
if t.Kind == types.Alias {
// if the underlying built-in is not deepcopy-able, deepcopy is opt-in through definition of custom methods.
// Note that aliases of builtins, maps, slices can have deepcopy methods.
if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil {
return true
} else {
return t.Underlying.Kind != types.Builtin || copyableType(t.Underlying)
}
}
if t.Kind != types.Struct {
return false
}
return true
}
func underlyingType(t *types.Type) *types.Type {
for t.Kind == types.Alias {
t = t.Underlying
}
return t
}
func (g *genDeepCopy) isOtherPackage(pkg string) bool {
if pkg == g.targetPackage {
return false
}
if strings.HasSuffix(pkg, "\""+g.targetPackage+"\"") {
return false
}
return true
}
func (g *genDeepCopy) Imports(c *generator.Context) (imports []string) {
importLines := []string{}
for _, singleImport := range g.imports.ImportLines() {
if g.isOtherPackage(singleImport) {
importLines = append(importLines, singleImport)
}
}
return importLines
}
func argsFromType(ts ...*types.Type) generator.Args {
a := generator.Args{
"type": ts[0],
}
for i, t := range ts {
a[fmt.Sprintf("type%d", i+1)] = t
}
return a
}
func (g *genDeepCopy) Init(c *generator.Context, w io.Writer) error {
return nil
}
func (g *genDeepCopy) needsGeneration(t *types.Type) bool {
tag := extractEnabledTypeTag(t)
tv := ""
if tag != nil {
tv = tag.value
if tv != "true" && tv != "false" {
klog.Fatalf("Type %v: unsupported %s value: %q", t, tagEnabledName, tag.value)
}
}
if g.allTypes && tv == "false" {
// The whole package is being generated, but this type has opted out.
klog.V(2).Infof("Not generating for type %v because type opted out", t)
return false
}
if !g.allTypes && tv != "true" {
// The whole package is NOT being generated, and this type has NOT opted in.
klog.V(2).Infof("Not generating for type %v because type did not opt in", t)
return false
}
return true
}
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 {
if len(v) == 0 {
continue
}
intfs := strings.Split(v, ",")
for _, intf := range intfs {
if intf == "" {
continue
}
result = append(result, intf)
}
}
return result
}
func extractNonPointerInterfaces(t *types.Type) (bool, error) {
comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...)
values := gengo.ExtractCommentTags("+", comments)[interfacesNonPointerTagName]
if len(values) == 0 {
return false, nil
}
result := values[0] == "true"
for _, v := range values {
if v == "true" != result {
return false, fmt.Errorf("contradicting %v value %q found to previous value %v", interfacesNonPointerTagName, v, result)
}
}
return result, nil
}
func (g *genDeepCopy) deepCopyableInterfacesInner(c *generator.Context, t *types.Type) ([]*types.Type, error) {
if t.Kind != types.Struct {
return nil, nil
}
intfs := extractInterfacesTag(t)
var ts []*types.Type
for _, intf := range intfs {
t := types.ParseFullyQualifiedName(intf)
klog.V(3).Infof("Loading package for interface %v", intf)
_, err := c.LoadPackages(t.Package)
if err != nil {
return nil, err
}
intfT := c.Universe.Type(t)
if intfT == nil {
return nil, fmt.Errorf("unknown type %q in %s tag of type %s", intf, interfacesTagName, intfT)
}
if intfT.Kind != types.Interface {
return nil, fmt.Errorf("type %q in %s tag of type %s is not an interface, but: %q", intf, interfacesTagName, t, intfT.Kind)
}
g.imports.AddType(intfT)
ts = append(ts, intfT)
}
return ts, nil
}
// deepCopyableInterfaces returns the interface types to implement and whether they apply to a non-pointer receiver.
func (g *genDeepCopy) deepCopyableInterfaces(c *generator.Context, t *types.Type) ([]*types.Type, bool, error) {
ts, err := g.deepCopyableInterfacesInner(c, t)
if err != nil {
return nil, false, err
}
set := map[string]*types.Type{}
for _, t := range ts {
set[t.String()] = t
}
result := []*types.Type{}
for _, t := range set {
result = append(result, t)
}
TypeSlice(result).Sort() // we need a stable sorting because it determines the order in generation
nonPointerReceiver, err := extractNonPointerInterfaces(t)
if err != nil {
return nil, false, err
}
return result, nonPointerReceiver, nil
}
type TypeSlice []*types.Type
func (s TypeSlice) Len() int { return len(s) }
func (s TypeSlice) Less(i, j int) bool { return s[i].String() < s[j].String() }
func (s TypeSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s TypeSlice) Sort() { sort.Sort(s) }
func (g *genDeepCopy) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
if !g.needsGeneration(t) {
return nil
}
klog.V(2).Infof("Generating deepcopy functions for type %v", t)
sw := generator.NewSnippetWriter(w, c, "$", "$")
args := argsFromType(t)
if deepCopyIntoMethodOrDie(t) == nil {
sw.Do("// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\n", args)
if isReference(t) {
sw.Do("func (in $.type|raw$) DeepCopyInto(out *$.type|raw$) {\n", args)
sw.Do("{in:=&in\n", nil)
} else {
sw.Do("func (in *$.type|raw$) DeepCopyInto(out *$.type|raw$) {\n", args)
}
if deepCopyMethodOrDie(t) != nil {
if t.Methods["DeepCopy"].Signature.Receiver.Kind == types.Pointer {
sw.Do("clone := in.DeepCopy()\n", nil)
sw.Do("*out = *clone\n", nil)
} else {
sw.Do("*out = in.DeepCopy()\n", nil)
}
sw.Do("return\n", nil)
} else {
g.generateFor(t, sw)
sw.Do("return\n", nil)
}
if isReference(t) {
sw.Do("}\n", nil)
}
sw.Do("}\n\n", nil)
}
if deepCopyMethodOrDie(t) == nil {
sw.Do("// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new $.type|raw$.\n", args)
if isReference(t) {
sw.Do("func (in $.type|raw$) DeepCopy() $.type|raw$ {\n", args)
} else {
sw.Do("func (in *$.type|raw$) DeepCopy() *$.type|raw$ {\n", args)
}
sw.Do("if in == nil { return nil }\n", nil)
sw.Do("out := new($.type|raw$)\n", args)
sw.Do("in.DeepCopyInto(out)\n", nil)
if isReference(t) {
sw.Do("return *out\n", nil)
} else {
sw.Do("return out\n", nil)
}
sw.Do("}\n\n", nil)
}
intfs, nonPointerReceiver, err := g.deepCopyableInterfaces(c, t)
if err != nil {
return err
}
for _, intf := range intfs {
sw.Do(fmt.Sprintf("// DeepCopy%s is an autogenerated deepcopy function, copying the receiver, creating a new $.type2|raw$.\n", intf.Name.Name), argsFromType(t, intf))
if nonPointerReceiver {
sw.Do(fmt.Sprintf("func (in $.type|raw$) DeepCopy%s() $.type2|raw$ {\n", intf.Name.Name), argsFromType(t, intf))
sw.Do("return *in.DeepCopy()", nil)
sw.Do("}\n\n", nil)
} else {
sw.Do(fmt.Sprintf("func (in *$.type|raw$) DeepCopy%s() $.type2|raw$ {\n", intf.Name.Name), argsFromType(t, intf))
sw.Do("if c := in.DeepCopy(); c != nil {\n", nil)
sw.Do("return c\n", nil)
sw.Do("}\n", nil)
sw.Do("return nil\n", nil)
sw.Do("}\n\n", nil)
}
}
return sw.Error()
}
// isReference return true for pointer, maps, slices and aliases of those.
func isReference(t *types.Type) bool {
if t.Kind == types.Pointer || t.Kind == types.Map || t.Kind == types.Slice {
return true
}
return t.Kind == types.Alias && isReference(underlyingType(t))
}
// we use the system of shadowing 'in' and 'out' so that the same code is valid
// at any nesting level. This makes the autogenerator easy to understand, and
// the compiler shouldn't care.
func (g *genDeepCopy) generateFor(t *types.Type, sw *generator.SnippetWriter) {
// derive inner types if t is an alias. We call the do* methods below with the alias type.
// basic rule: generate according to inner type, but construct objects with the alias type.
ut := underlyingType(t)
var f func(*types.Type, *generator.SnippetWriter)
switch ut.Kind {
case types.Builtin:
f = g.doBuiltin
case types.Map:
f = g.doMap
case types.Slice:
f = g.doSlice
case types.Struct:
f = g.doStruct
case types.Pointer:
f = g.doPointer
case types.Interface:
// interfaces are handled in-line in the other cases
klog.Fatalf("Hit an interface type %v. This should never happen.", t)
case types.Alias:
// can never happen because we branch on the underlying type which is never an alias
klog.Fatalf("Hit an alias type %v. This should never happen.", t)
default:
klog.Fatalf("Hit an unsupported type %v.", t)
}
f(t, sw)
}
// doBuiltin generates code for a builtin or an alias to a builtin. The generated code is
// is the same for both cases, i.e. it's the code for the underlying type.
func (g *genDeepCopy) doBuiltin(t *types.Type, sw *generator.SnippetWriter) {
if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil {
sw.Do("*out = in.DeepCopy()\n", nil)
return
}
sw.Do("*out = *in\n", nil)
}
// doMap generates code for a map or an alias to a map. The generated code is
// is the same for both cases, i.e. it's the code for the underlying type.
func (g *genDeepCopy) doMap(t *types.Type, sw *generator.SnippetWriter) {
ut := underlyingType(t)
uet := underlyingType(ut.Elem)
if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil {
sw.Do("*out = in.DeepCopy()\n", nil)
return
}
if !ut.Key.IsAssignable() {
klog.Fatalf("Hit an unsupported type %v for: %v", uet, t)
}
sw.Do("*out = make($.|raw$, len(*in))\n", t)
sw.Do("for key, val := range *in {\n", nil)
dc, dci := deepCopyMethodOrDie(ut.Elem), deepCopyIntoMethodOrDie(ut.Elem)
switch {
case dc != nil || dci != nil:
// Note: a DeepCopy exists because it is added if DeepCopyInto is manually defined
leftPointer := ut.Elem.Kind == types.Pointer
rightPointer := !isReference(ut.Elem)
if dc != nil {
rightPointer = dc.Results[0].Kind == types.Pointer
}
if leftPointer == rightPointer {
sw.Do("(*out)[key] = val.DeepCopy()\n", nil)
} else if leftPointer {
sw.Do("x := val.DeepCopy()\n", nil)
sw.Do("(*out)[key] = &x\n", nil)
} else {
sw.Do("(*out)[key] = *val.DeepCopy()\n", nil)
}
case ut.Elem.IsAnonymousStruct(): // not uet here because it needs type cast
sw.Do("(*out)[key] = val\n", nil)
case uet.IsAssignable():
sw.Do("(*out)[key] = val\n", nil)
case uet.Kind == types.Interface:
// Note: do not generate code that won't compile as `DeepCopyinterface{}()` is not a valid function
if uet.Name.Name == "interface{}" {
klog.Fatalf("DeepCopy of %q is unsupported. Instead, use named interfaces with DeepCopy<named-interface> as one of the methods.", uet.Name.Name)
}
sw.Do("if val == nil {(*out)[key]=nil} else {\n", nil)
// Note: if t.Elem has been an alias "J" of an interface "I" in Go, we will see it
// as kind Interface of name "J" here, i.e. generate val.DeepCopyJ(). The golang
// parser does not give us the underlying interface name. So we cannot do any better.
sw.Do(fmt.Sprintf("(*out)[key] = val.DeepCopy%s()\n", uet.Name.Name), nil)
sw.Do("}\n", nil)
case uet.Kind == types.Slice || uet.Kind == types.Map || uet.Kind == types.Pointer:
sw.Do("var outVal $.|raw$\n", uet)
sw.Do("if val == nil { (*out)[key] = nil } else {\n", nil)
sw.Do("in, out := &val, &outVal\n", uet)
g.generateFor(ut.Elem, sw)
sw.Do("}\n", nil)
sw.Do("(*out)[key] = outVal\n", nil)
case uet.Kind == types.Struct:
sw.Do("(*out)[key] = *val.DeepCopy()\n", uet)
default:
klog.Fatalf("Hit an unsupported type %v for %v", uet, t)
}
sw.Do("}\n", nil)
}
// doSlice generates code for a slice or an alias to a slice. The generated code is
// is the same for both cases, i.e. it's the code for the underlying type.
func (g *genDeepCopy) doSlice(t *types.Type, sw *generator.SnippetWriter) {
ut := underlyingType(t)
uet := underlyingType(ut.Elem)
if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil {
sw.Do("*out = in.DeepCopy()\n", nil)
return
}
sw.Do("*out = make($.|raw$, len(*in))\n", t)
if deepCopyMethodOrDie(ut.Elem) != nil || deepCopyIntoMethodOrDie(ut.Elem) != nil {
sw.Do("for i := range *in {\n", nil)
// Note: a DeepCopyInto exists because it is added if DeepCopy is manually defined
sw.Do("(*in)[i].DeepCopyInto(&(*out)[i])\n", nil)
sw.Do("}\n", nil)
} else if uet.Kind == types.Builtin || uet.IsAssignable() {
sw.Do("copy(*out, *in)\n", nil)
} else {
sw.Do("for i := range *in {\n", nil)
if uet.Kind == types.Slice || uet.Kind == types.Map || uet.Kind == types.Pointer || deepCopyMethodOrDie(ut.Elem) != nil || deepCopyIntoMethodOrDie(ut.Elem) != nil {
sw.Do("if (*in)[i] != nil {\n", nil)
sw.Do("in, out := &(*in)[i], &(*out)[i]\n", nil)
g.generateFor(ut.Elem, sw)
sw.Do("}\n", nil)
} else if uet.Kind == types.Interface {
// Note: do not generate code that won't compile as `DeepCopyinterface{}()` is not a valid function
if uet.Name.Name == "interface{}" {
klog.Fatalf("DeepCopy of %q is unsupported. Instead, use named interfaces with DeepCopy<named-interface> as one of the methods.", uet.Name.Name)
}
sw.Do("if (*in)[i] != nil {\n", nil)
// Note: if t.Elem has been an alias "J" of an interface "I" in Go, we will see it
// as kind Interface of name "J" here, i.e. generate val.DeepCopyJ(). The golang
// parser does not give us the underlying interface name. So we cannot do any better.
sw.Do(fmt.Sprintf("(*out)[i] = (*in)[i].DeepCopy%s()\n", uet.Name.Name), nil)
sw.Do("}\n", nil)
} else if uet.Kind == types.Struct {
sw.Do("(*in)[i].DeepCopyInto(&(*out)[i])\n", nil)
} else {
klog.Fatalf("Hit an unsupported type %v for %v", uet, t)
}
sw.Do("}\n", nil)
}
}
// doStruct generates code for a struct or an alias to a struct. The generated code is
// is the same for both cases, i.e. it's the code for the underlying type.
func (g *genDeepCopy) doStruct(t *types.Type, sw *generator.SnippetWriter) {
ut := underlyingType(t)
if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil {
sw.Do("*out = in.DeepCopy()\n", nil)
return
}
// Simple copy covers a lot of cases.
sw.Do("*out = *in\n", nil)
// Now fix-up fields as needed.
for _, m := range ut.Members {
ft := m.Type
uft := underlyingType(ft)
args := generator.Args{
"type": ft,
"kind": ft.Kind,
"name": m.Name,
}
dc, dci := deepCopyMethodOrDie(ft), deepCopyIntoMethodOrDie(ft)
switch {
case dc != nil || dci != nil:
// Note: a DeepCopyInto exists because it is added if DeepCopy is manually defined
leftPointer := ft.Kind == types.Pointer
rightPointer := !isReference(ft)
if dc != nil {
rightPointer = dc.Results[0].Kind == types.Pointer
}
if leftPointer == rightPointer {
sw.Do("out.$.name$ = in.$.name$.DeepCopy()\n", args)
} else if leftPointer {
sw.Do("x := in.$.name$.DeepCopy()\n", args)
sw.Do("out.$.name$ = = &x\n", args)
} else {
sw.Do("in.$.name$.DeepCopyInto(&out.$.name$)\n", args)
}
case uft.Kind == types.Builtin:
// the initial *out = *in was enough
case uft.Kind == types.Map, uft.Kind == types.Slice, uft.Kind == types.Pointer:
// Fixup non-nil reference-semantic types.
sw.Do("if in.$.name$ != nil {\n", args)
sw.Do("in, out := &in.$.name$, &out.$.name$\n", args)
g.generateFor(ft, sw)
sw.Do("}\n", nil)
case uft.Kind == types.Array:
sw.Do("out.$.name$ = in.$.name$\n", args)
case uft.Kind == types.Struct:
if ft.IsAssignable() {
sw.Do("out.$.name$ = in.$.name$\n", args)
} else {
sw.Do("in.$.name$.DeepCopyInto(&out.$.name$)\n", args)
}
case uft.Kind == types.Interface:
// Note: do not generate code that won't compile as `DeepCopyinterface{}()` is not a valid function
if uft.Name.Name == "interface{}" {
klog.Fatalf("DeepCopy of %q is unsupported. Instead, use named interfaces with DeepCopy<named-interface> as one of the methods.", uft.Name.Name)
}
sw.Do("if in.$.name$ != nil {\n", args)
// Note: if t.Elem has been an alias "J" of an interface "I" in Go, we will see it
// as kind Interface of name "J" here, i.e. generate val.DeepCopyJ(). The golang
// parser does not give us the underlying interface name. So we cannot do any better.
sw.Do(fmt.Sprintf("out.$.name$ = in.$.name$.DeepCopy%s()\n", uft.Name.Name), args)
sw.Do("}\n", nil)
default:
klog.Fatalf("Hit an unsupported type '%v' for '%v', from %v.%v", uft, ft, t, m.Name)
}
}
}
// doPointer generates code for a pointer or an alias to a pointer. The generated code is
// is the same for both cases, i.e. it's the code for the underlying type.
func (g *genDeepCopy) doPointer(t *types.Type, sw *generator.SnippetWriter) {
ut := underlyingType(t)
uet := underlyingType(ut.Elem)
dc, dci := deepCopyMethodOrDie(ut.Elem), deepCopyIntoMethodOrDie(ut.Elem)
switch {
case dc != nil || dci != nil:
rightPointer := !isReference(ut.Elem)
if dc != nil {
rightPointer = dc.Results[0].Kind == types.Pointer
}
if rightPointer {
sw.Do("*out = (*in).DeepCopy()\n", nil)
} else {
sw.Do("x := (*in).DeepCopy()\n", nil)
sw.Do("*out = &x\n", nil)
}
case uet.IsAssignable():
sw.Do("*out = new($.Elem|raw$)\n", ut)
sw.Do("**out = **in", nil)
case uet.Kind == types.Map, uet.Kind == types.Slice, uet.Kind == types.Pointer:
sw.Do("*out = new($.Elem|raw$)\n", ut)
sw.Do("if **in != nil {\n", nil)
sw.Do("in, out := *in, *out\n", nil)
g.generateFor(uet, sw)
sw.Do("}\n", nil)
case uet.Kind == types.Struct:
sw.Do("*out = new($.Elem|raw$)\n", ut)
sw.Do("(*in).DeepCopyInto(*out)\n", nil)
default:
klog.Fatalf("Hit an unsupported type %v for %v", uet, t)
}
}

View File

@@ -16,14 +16,24 @@ limitations under the License.
// deepcopy-gen is a tool for auto-generating DeepCopy functions.
//
// Given a list of input directories, it will generate functions that
// efficiently perform a full deep-copy of each type. For any type that
// offers a `.DeepCopy()` method, it will simply call that. Otherwise it will
// use standard value assignment whenever possible. If that is not possible it
// will try to call its own generated copy function for the type, if the type is
// within the allowed root packages. Failing that, it will fall back on
// `conversion.Cloner.DeepCopy(val)` to make the copy. The resulting file will
// be stored in the same directory as the processed source package.
// Given a list of input directories, it will generate DeepCopy and
// DeepCopyInto methods that efficiently perform a full deep-copy of each type.
// If these methods already exist (are predefined by the developer), they are
// used instead of generating new ones. Generated code will use standard value
// assignment whenever possible. If that is not possible it will try to call
// its own generated copy function for the type. Failing that, it will fall
// back on `conversion.Cloner.DeepCopy(val)` to make the copy. The resulting
// file will be stored in the same directory as the processed source package.
//
// If interfaces are referenced in types, it is expected that corresponding
// DeepCopyInterfaceName methods exist, e.g. DeepCopyObject for runtime.Object.
// These can be predefined by the developer or generated through tags, see
// below. They must be added to the interfaces themselves manually, e.g.
//
// type Object interface {
// ...
// DeepCopyObject() Object
// }
//
// Generation is governed by comment tags in the source. Any package may
// request DeepCopy generation by including a comment in the file-comments of
@@ -32,48 +42,67 @@ limitations under the License.
// // +k8s:deepcopy-gen=package
//
// DeepCopy functions can be generated for individual types, rather than the
// entire package by specifying a comment on the type definion of the form:
// entire package by specifying a comment on the type definition of the form:
//
// // +k8s:deepcopy-gen=true
//
// When generating for a whole package, individual types may opt out of
// DeepCopy generation by specifying a comment on the of the form:
// DeepCopy generation by specifying a comment on the type definition of the
// form:
//
// // +k8s:deepcopy-gen=false
//
// Note that registration is a whole-package option, and is not available for
// individual types.
// Additional DeepCopyInterfaceName methods can be generated by specifying a
// comment on the type definition of the form:
//
// // +k8s:deepcopy-gen:interfaces=k8s.io/kubernetes/runtime.Object,k8s.io/kubernetes/runtime.List
//
// This leads to the generation of DeepCopyObject and DeepCopyList with the given
// interfaces as return types. We say that the tagged type implements deepcopy for the
// interfaces.
//
// The deepcopy funcs for interfaces using "+k8s:deepcopy-gen:interfaces" use the pointer
// of the type as receiver. For those special cases where the non-pointer object should
// implement the interface, this can be done with:
//
// // +k8s:deepcopy-gen:nonpointer-interfaces=true
package main
import (
"flag"
"github.com/spf13/pflag"
"k8s.io/gengo/examples/deepcopy-gen/generators"
"k8s.io/code-generator/cmd/deepcopy-gen/args"
"k8s.io/code-generator/cmd/deepcopy-gen/generators"
"k8s.io/gengo/v2"
"k8s.io/gengo/v2/generator"
"k8s.io/klog/v2"
generatorargs "k8s.io/code-generator/cmd/deepcopy-gen/args"
)
func main() {
klog.InitFlags(nil)
genericArgs, customArgs := generatorargs.NewDefaults()
args := args.New()
genericArgs.AddFlags(pflag.CommandLine)
customArgs.AddFlags(pflag.CommandLine)
args.AddFlags(pflag.CommandLine)
flag.Set("logtostderr", "true")
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
pflag.Parse()
if err := generatorargs.Validate(genericArgs); err != nil {
if err := args.Validate(); err != nil {
klog.Fatalf("Error: %v", err)
}
myTargets := func(context *generator.Context) []generator.Target {
return generators.GetTargets(context, args)
}
// Run it.
if err := genericArgs.Execute(
if err := gengo.Execute(
generators.NameSystems(),
generators.DefaultNameSystem(),
generators.Packages,
myTargets,
gengo.StdBuildTag,
pflag.Args(),
); err != nil {
klog.Fatalf("Error: %v", err)
}

View File

@@ -18,18 +18,18 @@ package args
import (
"fmt"
"path"
"github.com/spf13/pflag"
codegenutil "k8s.io/code-generator/pkg/util"
"k8s.io/gengo/args"
)
// CustomArgs is used by the gengo framework to pass args specific to this generator.
type CustomArgs struct {
VersionedClientSetPackage string
InternalClientSetPackage string
ListersPackage string
// Args is used by the gengo framework to pass args specific to this generator.
type Args struct {
OutputDir string // must be a directory path
OutputPkg string // must be a Go import-path
GoHeaderFile string
VersionedClientSetPackage string // must be a Go import-path
InternalClientSetPackage string // must be a Go import-path
ListersPackage string // must be a Go import-path
SingleDirectory bool
// PluralExceptions define a list of pluralizer exceptions in Type:PluralType format.
@@ -37,47 +37,46 @@ type CustomArgs struct {
PluralExceptions []string
}
// NewDefaults returns default arguments for the generator.
func NewDefaults() (*args.GeneratorArgs, *CustomArgs) {
genericArgs := args.Default().WithoutDefaultFlagParsing()
customArgs := &CustomArgs{
SingleDirectory: false,
PluralExceptions: []string{"Endpoints:Endpoints"},
// New returns default arguments for the generator.
func New() *Args {
return &Args{
SingleDirectory: false,
}
genericArgs.CustomArgs = customArgs
if pkg := codegenutil.CurrentPackage(); len(pkg) != 0 {
genericArgs.OutputPackagePath = path.Join(pkg, "pkg/client/informers")
customArgs.VersionedClientSetPackage = path.Join(pkg, "pkg/client/clientset/versioned")
customArgs.InternalClientSetPackage = path.Join(pkg, "pkg/client/clientset/internalversion")
customArgs.ListersPackage = path.Join(pkg, "pkg/client/listers")
}
return genericArgs, customArgs
}
// AddFlags add the generator flags to the flag set.
func (ca *CustomArgs) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&ca.InternalClientSetPackage, "internal-clientset-package", ca.InternalClientSetPackage, "the full package name for the internal clientset to use")
fs.StringVar(&ca.VersionedClientSetPackage, "versioned-clientset-package", ca.VersionedClientSetPackage, "the full package name for the versioned clientset to use")
fs.StringVar(&ca.ListersPackage, "listers-package", ca.ListersPackage, "the full package name for the listers to use")
fs.BoolVar(&ca.SingleDirectory, "single-directory", ca.SingleDirectory, "if true, omit the intermediate \"internalversion\" and \"externalversions\" subdirectories")
fs.StringSliceVar(&ca.PluralExceptions, "plural-exceptions", ca.PluralExceptions, "list of comma separated plural exception definitions in Type:PluralizedType format")
func (args *Args) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&args.OutputDir, "output-dir", "",
"the base directory under which to generate results")
fs.StringVar(&args.OutputPkg, "output-pkg", args.OutputPkg,
"the Go import-path of the generated results")
fs.StringVar(&args.GoHeaderFile, "go-header-file", "",
"the path to a file containing boilerplate header text; the string \"YEAR\" will be replaced with the current 4-digit year")
fs.StringVar(&args.InternalClientSetPackage, "internal-clientset-package", args.InternalClientSetPackage,
"the Go import-path of the internal clientset to use")
fs.StringVar(&args.VersionedClientSetPackage, "versioned-clientset-package", args.VersionedClientSetPackage,
"the Go import-path of the versioned clientset to use")
fs.StringVar(&args.ListersPackage, "listers-package", args.ListersPackage,
"the Go import-path of the listers to use")
fs.BoolVar(&args.SingleDirectory, "single-directory", args.SingleDirectory,
"if true, omit the intermediate \"internalversion\" and \"externalversions\" subdirectories")
fs.StringSliceVar(&args.PluralExceptions, "plural-exceptions", args.PluralExceptions,
"list of comma separated plural exception definitions in Type:PluralizedType format")
}
// Validate checks the given arguments.
func Validate(genericArgs *args.GeneratorArgs) error {
customArgs := genericArgs.CustomArgs.(*CustomArgs)
if len(genericArgs.OutputPackagePath) == 0 {
return fmt.Errorf("output package cannot be empty")
func (args *Args) Validate() error {
if len(args.OutputDir) == 0 {
return fmt.Errorf("--output-dir must be specified")
}
if len(customArgs.VersionedClientSetPackage) == 0 {
return fmt.Errorf("versioned clientset package cannot be empty")
if len(args.OutputPkg) == 0 {
return fmt.Errorf("--output-pkg must be specified")
}
if len(customArgs.ListersPackage) == 0 {
return fmt.Errorf("listers package cannot be empty")
if len(args.VersionedClientSetPackage) == 0 {
return fmt.Errorf("--versioned-clientset-package must be specified")
}
if len(args.ListersPackage) == 0 {
return fmt.Errorf("--listers-package must be specified")
}
return nil
}

View File

@@ -21,9 +21,9 @@ import (
"path"
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
"k8s.io/gengo/v2/types"
"k8s.io/klog/v2"
)
@@ -31,7 +31,7 @@ import (
// factoryGenerator produces a file of listers for a given GroupVersion and
// type.
type factoryGenerator struct {
generator.DefaultGen
generator.GoGenerator
outputPackage string
imports namer.ImportTracker
groupVersions map[string]clientgentypes.GroupVersions
@@ -299,7 +299,8 @@ type SharedInformerFactory interface {
// Start initializes all requested informers. They are handled in goroutines
// which run until the stop channel gets closed.
Start(stopCh <-chan struct{})
// Warning: Start does not block. When run in a go-routine, it will race with a later WaitForCacheSync.
Start(stopCh <-chan struct{})
// Shutdown marks a factory as shutting down. At that point no new
// informers can be started anymore and Start will return without

View File

@@ -19,9 +19,9 @@ package generators
import (
"io"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
"k8s.io/gengo/v2/types"
"k8s.io/klog/v2"
)
@@ -29,7 +29,7 @@ import (
// factoryInterfaceGenerator produces a file of interfaces used to break a dependency cycle for
// informer registration
type factoryInterfaceGenerator struct {
generator.DefaultGen
generator.GoGenerator
outputPackage string
imports namer.ImportTracker
clientSetPackage string

View File

@@ -23,14 +23,14 @@ import (
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
codegennamer "k8s.io/code-generator/pkg/namer"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
"k8s.io/gengo/v2/types"
)
// genericGenerator generates the generic informer.
type genericGenerator struct {
generator.DefaultGen
generator.GoGenerator
outputPackage string
imports namer.ImportTracker
groupVersions map[string]clientgentypes.GroupVersions

View File

@@ -18,18 +18,18 @@ package generators
import (
"io"
"path/filepath"
"path"
"strings"
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
"k8s.io/gengo/v2/types"
)
// groupInterfaceGenerator generates the per-group interface file.
type groupInterfaceGenerator struct {
generator.DefaultGen
generator.GoGenerator
outputPackage string
imports namer.ImportTracker
groupVersions clientgentypes.GroupVersions
@@ -70,7 +70,7 @@ func (g *groupInterfaceGenerator) GenerateType(c *generator.Context, t *types.Ty
versions := make([]versionData, 0, len(g.groupVersions.Versions))
for _, version := range g.groupVersions.Versions {
gv := clientgentypes.GroupVersion{Group: g.groupVersions.Group, Version: version.Version}
versionPackage := filepath.Join(g.outputPackage, strings.ToLower(gv.Version.NonEmpty()))
versionPackage := path.Join(g.outputPackage, strings.ToLower(gv.Version.NonEmpty()))
iface := c.Universe.Type(types.Name{Package: versionPackage, Name: "Interface"})
versions = append(versions, versionData{
Name: namer.IC(version.Version.NonEmpty()),

View File

@@ -21,9 +21,9 @@ import (
"io"
"strings"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
"k8s.io/gengo/v2/types"
"k8s.io/code-generator/cmd/client-gen/generators/util"
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
@@ -34,7 +34,7 @@ import (
// informerGenerator produces a file of listers for a given GroupVersion and
// type.
type informerGenerator struct {
generator.DefaultGen
generator.GoGenerator
outputPackage string
groupPkgName string
groupVersion clientgentypes.GroupVersion

View File

@@ -22,16 +22,15 @@ import (
"path/filepath"
"strings"
"k8s.io/gengo/args"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/klog/v2"
"k8s.io/code-generator/cmd/client-gen/generators/util"
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
informergenargs "k8s.io/code-generator/cmd/informer-gen/args"
"k8s.io/code-generator/cmd/informer-gen/args"
genutil "k8s.io/code-generator/pkg/util"
"k8s.io/gengo/v2"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
"k8s.io/gengo/v2/types"
"k8s.io/klog/v2"
)
// NameSystems returns the name system used by the generators in this package.
@@ -85,37 +84,34 @@ func isInternal(m types.Member) bool {
return !strings.Contains(m.Tags, "json")
}
func packageForInternalInterfaces(base string) string {
return filepath.Join(base, "internalinterfaces")
}
const subdirForInternalInterfaces = "internalinterfaces"
// Packages makes the client package definition.
func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
boilerplate, err := arguments.LoadGoBoilerplate()
// GetTargets makes the client target definition.
func GetTargets(context *generator.Context, args *args.Args) []generator.Target {
boilerplate, err := gengo.GoBoilerplate(args.GoHeaderFile, "", gengo.StdGeneratedBy)
if err != nil {
klog.Fatalf("Failed loading boilerplate: %v", err)
}
customArgs, ok := arguments.CustomArgs.(*informergenargs.CustomArgs)
if !ok {
klog.Fatalf("Wrong CustomArgs type: %T", arguments.CustomArgs)
internalVersionOutputDir := args.OutputDir
internalVersionOutputPkg := args.OutputPkg
externalVersionOutputDir := args.OutputDir
externalVersionOutputPkg := args.OutputPkg
if !args.SingleDirectory {
internalVersionOutputDir = filepath.Join(internalVersionOutputDir, "internalversion")
internalVersionOutputPkg = path.Join(internalVersionOutputPkg, "internalversion")
externalVersionOutputDir = filepath.Join(externalVersionOutputDir, "externalversions")
externalVersionOutputPkg = path.Join(externalVersionOutputPkg, "externalversions")
}
internalVersionPackagePath := filepath.Join(arguments.OutputPackagePath)
externalVersionPackagePath := filepath.Join(arguments.OutputPackagePath)
if !customArgs.SingleDirectory {
internalVersionPackagePath = filepath.Join(arguments.OutputPackagePath, "internalversion")
externalVersionPackagePath = filepath.Join(arguments.OutputPackagePath, "externalversions")
}
var packageList generator.Packages
var targetList []generator.Target
typesForGroupVersion := make(map[clientgentypes.GroupVersion][]*types.Type)
externalGroupVersions := make(map[string]clientgentypes.GroupVersions)
internalGroupVersions := make(map[string]clientgentypes.GroupVersions)
groupGoNames := make(map[string]string)
for _, inputDir := range arguments.InputDirs {
p := context.Universe.Package(genutil.Vendorless(inputDir))
for _, inputPkg := range context.Inputs {
p := context.Universe.Package(inputPkg)
objectMeta, internal, err := objectMetaForPackage(p)
if err != nil {
@@ -148,14 +144,14 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
// 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 := types.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil {
if override := gengo.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil {
gv.Group = clientgentypes.Group(override[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 := types.ExtractCommentTags("+", p.Comments)["groupGoName"]; override != nil {
if override := gengo.ExtractCommentTags("+", p.Comments)["groupGoName"]; override != nil {
groupGoNames[groupPackageName] = namer.IC(override[0])
}
@@ -191,57 +187,80 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
typesToGenerate = orderer.OrderTypes(typesToGenerate)
if internal {
packageList = append(packageList, versionPackage(internalVersionPackagePath, groupPackageName, gv, groupGoNames[groupPackageName], boilerplate, typesToGenerate, customArgs.InternalClientSetPackage, customArgs.ListersPackage))
targetList = append(targetList,
versionTarget(
internalVersionOutputDir, internalVersionOutputPkg,
groupPackageName, gv, groupGoNames[groupPackageName],
boilerplate, typesToGenerate,
args.InternalClientSetPackage, args.ListersPackage))
} else {
packageList = append(packageList, versionPackage(externalVersionPackagePath, groupPackageName, gv, groupGoNames[groupPackageName], boilerplate, typesToGenerate, customArgs.VersionedClientSetPackage, customArgs.ListersPackage))
targetList = append(targetList,
versionTarget(
externalVersionOutputDir, externalVersionOutputPkg,
groupPackageName, gv, groupGoNames[groupPackageName],
boilerplate, typesToGenerate,
args.VersionedClientSetPackage, args.ListersPackage))
}
}
if len(externalGroupVersions) != 0 {
packageList = append(packageList, factoryInterfacePackage(externalVersionPackagePath, boilerplate, customArgs.VersionedClientSetPackage))
packageList = append(packageList, factoryPackage(externalVersionPackagePath, boilerplate, groupGoNames, genutil.PluralExceptionListToMapOrDie(customArgs.PluralExceptions), externalGroupVersions,
customArgs.VersionedClientSetPackage,
typesForGroupVersion))
targetList = append(targetList,
factoryInterfaceTarget(
externalVersionOutputDir, externalVersionOutputPkg,
boilerplate, args.VersionedClientSetPackage))
targetList = append(targetList,
factoryTarget(
externalVersionOutputDir, externalVersionOutputPkg,
boilerplate, groupGoNames, genutil.PluralExceptionListToMapOrDie(args.PluralExceptions),
externalGroupVersions, args.VersionedClientSetPackage, typesForGroupVersion))
for _, gvs := range externalGroupVersions {
packageList = append(packageList, groupPackage(externalVersionPackagePath, gvs, boilerplate))
targetList = append(targetList,
groupTarget(externalVersionOutputDir, externalVersionOutputPkg, gvs, boilerplate))
}
}
if len(internalGroupVersions) != 0 {
packageList = append(packageList, factoryInterfacePackage(internalVersionPackagePath, boilerplate, customArgs.InternalClientSetPackage))
packageList = append(packageList, factoryPackage(internalVersionPackagePath, boilerplate, groupGoNames, genutil.PluralExceptionListToMapOrDie(customArgs.PluralExceptions), internalGroupVersions, customArgs.InternalClientSetPackage, typesForGroupVersion))
targetList = append(targetList,
factoryInterfaceTarget(internalVersionOutputDir, internalVersionOutputPkg, boilerplate, args.InternalClientSetPackage))
targetList = append(targetList,
factoryTarget(
internalVersionOutputDir, internalVersionOutputPkg,
boilerplate, groupGoNames, genutil.PluralExceptionListToMapOrDie(args.PluralExceptions),
internalGroupVersions, args.InternalClientSetPackage, typesForGroupVersion))
for _, gvs := range internalGroupVersions {
packageList = append(packageList, groupPackage(internalVersionPackagePath, gvs, boilerplate))
targetList = append(targetList,
groupTarget(internalVersionOutputDir, internalVersionOutputPkg, gvs, boilerplate))
}
}
return packageList
return targetList
}
func factoryPackage(basePackage string, boilerplate []byte, groupGoNames, pluralExceptions map[string]string, groupVersions map[string]clientgentypes.GroupVersions, clientSetPackage string,
typesForGroupVersion map[clientgentypes.GroupVersion][]*types.Type) generator.Package {
return &generator.DefaultPackage{
PackageName: filepath.Base(basePackage),
PackagePath: basePackage,
HeaderText: boilerplate,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
func factoryTarget(outputDirBase, outputPkgBase string, boilerplate []byte, groupGoNames, pluralExceptions map[string]string, groupVersions map[string]clientgentypes.GroupVersions, clientSetPackage string,
typesForGroupVersion map[clientgentypes.GroupVersion][]*types.Type) generator.Target {
return &generator.SimpleTarget{
PkgName: path.Base(outputDirBase),
PkgPath: outputPkgBase,
PkgDir: outputDirBase,
HeaderComment: boilerplate,
GeneratorsFunc: func(c *generator.Context) (generators []generator.Generator) {
generators = append(generators, &factoryGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: "factory",
GoGenerator: generator.GoGenerator{
OutputFilename: "factory.go",
},
outputPackage: basePackage,
outputPackage: outputPkgBase,
imports: generator.NewImportTracker(),
groupVersions: groupVersions,
clientSetPackage: clientSetPackage,
internalInterfacesPackage: packageForInternalInterfaces(basePackage),
internalInterfacesPackage: path.Join(outputPkgBase, subdirForInternalInterfaces),
gvGoNames: groupGoNames,
})
generators = append(generators, &genericGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: "generic",
GoGenerator: generator.GoGenerator{
OutputFilename: "generic.go",
},
outputPackage: basePackage,
outputPackage: outputPkgBase,
imports: generator.NewImportTracker(),
groupVersions: groupVersions,
pluralExceptions: pluralExceptions,
@@ -254,19 +273,21 @@ func factoryPackage(basePackage string, boilerplate []byte, groupGoNames, plural
}
}
func factoryInterfacePackage(basePackage string, boilerplate []byte, clientSetPackage string) generator.Package {
packagePath := packageForInternalInterfaces(basePackage)
func factoryInterfaceTarget(outputDirBase, outputPkgBase string, boilerplate []byte, clientSetPackage string) generator.Target {
outputDir := filepath.Join(outputDirBase, subdirForInternalInterfaces)
outputPkg := path.Join(outputPkgBase, subdirForInternalInterfaces)
return &generator.DefaultPackage{
PackageName: filepath.Base(packagePath),
PackagePath: packagePath,
HeaderText: boilerplate,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
return &generator.SimpleTarget{
PkgName: path.Base(outputDir),
PkgPath: outputPkg,
PkgDir: outputDir,
HeaderComment: boilerplate,
GeneratorsFunc: func(c *generator.Context) (generators []generator.Generator) {
generators = append(generators, &factoryInterfaceGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: "factory_interfaces",
GoGenerator: generator.GoGenerator{
OutputFilename: "factory_interfaces.go",
},
outputPackage: packagePath,
outputPackage: outputPkg,
imports: generator.NewImportTracker(),
clientSetPackage: clientSetPackage,
})
@@ -276,23 +297,25 @@ func factoryInterfacePackage(basePackage string, boilerplate []byte, clientSetPa
}
}
func groupPackage(basePackage string, groupVersions clientgentypes.GroupVersions, boilerplate []byte) generator.Package {
packagePath := filepath.Join(basePackage, groupVersions.PackageName)
func groupTarget(outputDirBase, outputPackageBase string, groupVersions clientgentypes.GroupVersions, boilerplate []byte) generator.Target {
outputDir := filepath.Join(outputDirBase, groupVersions.PackageName)
outputPkg := path.Join(outputPackageBase, groupVersions.PackageName)
groupPkgName := strings.Split(string(groupVersions.PackageName), ".")[0]
return &generator.DefaultPackage{
PackageName: groupPkgName,
PackagePath: packagePath,
HeaderText: boilerplate,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
return &generator.SimpleTarget{
PkgName: groupPkgName,
PkgPath: outputPkg,
PkgDir: outputDir,
HeaderComment: boilerplate,
GeneratorsFunc: func(c *generator.Context) (generators []generator.Generator) {
generators = append(generators, &groupInterfaceGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: "interface",
GoGenerator: generator.GoGenerator{
OutputFilename: "interface.go",
},
outputPackage: packagePath,
outputPackage: outputPkg,
groupVersions: groupVersions,
imports: generator.NewImportTracker(),
internalInterfacesPackage: packageForInternalInterfaces(basePackage),
internalInterfacesPackage: path.Join(outputPackageBase, subdirForInternalInterfaces),
})
return generators
},
@@ -303,30 +326,33 @@ func groupPackage(basePackage string, groupVersions clientgentypes.GroupVersions
}
}
func versionPackage(basePackage string, groupPkgName string, gv clientgentypes.GroupVersion, groupGoName string, boilerplate []byte, typesToGenerate []*types.Type, clientSetPackage, listersPackage string) generator.Package {
packagePath := filepath.Join(basePackage, groupPkgName, strings.ToLower(gv.Version.NonEmpty()))
func versionTarget(outputDirBase, outputPkgBase string, groupPkgName string, gv clientgentypes.GroupVersion, groupGoName string, boilerplate []byte, typesToGenerate []*types.Type, clientSetPackage, listersPackage string) generator.Target {
subdir := []string{groupPkgName, strings.ToLower(gv.Version.NonEmpty())}
outputDir := filepath.Join(outputDirBase, filepath.Join(subdir...))
outputPkg := path.Join(outputPkgBase, path.Join(subdir...))
return &generator.DefaultPackage{
PackageName: strings.ToLower(gv.Version.NonEmpty()),
PackagePath: packagePath,
HeaderText: boilerplate,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
return &generator.SimpleTarget{
PkgName: strings.ToLower(gv.Version.NonEmpty()),
PkgPath: outputPkg,
PkgDir: outputDir,
HeaderComment: boilerplate,
GeneratorsFunc: func(c *generator.Context) (generators []generator.Generator) {
generators = append(generators, &versionInterfaceGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: "interface",
GoGenerator: generator.GoGenerator{
OutputFilename: "interface.go",
},
outputPackage: packagePath,
outputPackage: outputPkg,
imports: generator.NewImportTracker(),
types: typesToGenerate,
internalInterfacesPackage: packageForInternalInterfaces(basePackage),
internalInterfacesPackage: path.Join(outputPkgBase, subdirForInternalInterfaces),
})
for _, t := range typesToGenerate {
generators = append(generators, &informerGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: strings.ToLower(t.Name.Name),
GoGenerator: generator.GoGenerator{
OutputFilename: strings.ToLower(t.Name.Name) + ".go",
},
outputPackage: packagePath,
outputPackage: outputPkg,
groupPkgName: groupPkgName,
groupVersion: gv,
groupGoName: groupGoName,
@@ -334,7 +360,7 @@ func versionPackage(basePackage string, groupPkgName string, gv clientgentypes.G
imports: generator.NewImportTracker(),
clientSetPackage: clientSetPackage,
listersPackage: listersPackage,
internalInterfacesPackage: packageForInternalInterfaces(basePackage),
internalInterfacesPackage: path.Join(outputPkgBase, subdirForInternalInterfaces),
})
}
return generators

View File

@@ -16,7 +16,7 @@ limitations under the License.
package generators
import "k8s.io/gengo/types"
import "k8s.io/gengo/v2/types"
var (
apiScheme = types.Name{Package: "k8s.io/kubernetes/pkg/api/legacyscheme", Name: "Scheme"}

View File

@@ -19,16 +19,16 @@ package generators
import (
"io"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
"k8s.io/gengo/v2/types"
"k8s.io/code-generator/cmd/client-gen/generators/util"
)
// versionInterfaceGenerator generates the per-version interface file.
type versionInterfaceGenerator struct {
generator.DefaultGen
generator.GoGenerator
outputPackage string
imports namer.ImportTracker
types []*types.Type

View File

@@ -20,39 +20,38 @@ import (
"flag"
"github.com/spf13/pflag"
"k8s.io/code-generator/cmd/informer-gen/args"
"k8s.io/code-generator/cmd/informer-gen/generators"
"k8s.io/code-generator/pkg/util"
"k8s.io/gengo/v2"
"k8s.io/gengo/v2/generator"
"k8s.io/klog/v2"
generatorargs "k8s.io/code-generator/cmd/informer-gen/args"
)
func main() {
klog.InitFlags(nil)
genericArgs, customArgs := generatorargs.NewDefaults()
args := args.New()
// Override defaults.
// TODO: move out of informer-gen
genericArgs.OutputPackagePath = "k8s.io/kubernetes/pkg/client/informers/informers_generated"
customArgs.VersionedClientSetPackage = "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
customArgs.InternalClientSetPackage = "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
customArgs.ListersPackage = "k8s.io/kubernetes/pkg/client/listers"
genericArgs.AddFlags(pflag.CommandLine)
customArgs.AddFlags(pflag.CommandLine)
args.AddFlags(pflag.CommandLine)
flag.Set("logtostderr", "true")
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
pflag.Parse()
if err := generatorargs.Validate(genericArgs); err != nil {
if err := args.Validate(); err != nil {
klog.Fatalf("Error: %v", err)
}
myTargets := func(context *generator.Context) []generator.Target {
return generators.GetTargets(context, args)
}
// Run it.
if err := genericArgs.Execute(
generators.NameSystems(util.PluralExceptionListToMapOrDie(customArgs.PluralExceptions)),
if err := gengo.Execute(
generators.NameSystems(util.PluralExceptionListToMapOrDie(args.PluralExceptions)),
generators.DefaultNameSystem(),
generators.Packages,
myTargets,
gengo.StdBuildTag,
pflag.Args(),
); err != nil {
klog.Fatalf("Error: %v", err)
}

View File

@@ -18,47 +18,45 @@ package args
import (
"fmt"
"path"
"github.com/spf13/pflag"
codegenutil "k8s.io/code-generator/pkg/util"
"k8s.io/gengo/args"
)
// CustomArgs is used by the gengo framework to pass args specific to this generator.
type CustomArgs struct {
// Args is used by the gengo framework to pass args specific to this generator.
type Args struct {
OutputDir string // must be a directory path
OutputPkg string // must be a Go import-path
GoHeaderFile string
// PluralExceptions specify list of exceptions used when pluralizing certain types.
// For example 'Endpoints:Endpoints', otherwise the pluralizer will generate 'Endpointes'.
PluralExceptions []string
}
// NewDefaults returns default arguments for the generator.
func NewDefaults() (*args.GeneratorArgs, *CustomArgs) {
genericArgs := args.Default().WithoutDefaultFlagParsing()
customArgs := &CustomArgs{
PluralExceptions: []string{"Endpoints:Endpoints"},
}
genericArgs.CustomArgs = customArgs
if pkg := codegenutil.CurrentPackage(); len(pkg) != 0 {
genericArgs.OutputPackagePath = path.Join(pkg, "pkg/client/listers")
}
return genericArgs, customArgs
// New returns default arguments for the generator.
func New() *Args {
return &Args{}
}
// AddFlags add the generator flags to the flag set.
func (ca *CustomArgs) AddFlags(fs *pflag.FlagSet) {
fs.StringSliceVar(&ca.PluralExceptions, "plural-exceptions", ca.PluralExceptions, "list of comma separated plural exception definitions in Type:PluralizedType format")
func (args *Args) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&args.OutputDir, "output-dir", "",
"the base directory under which to generate results")
fs.StringVar(&args.OutputPkg, "output-pkg", "",
"the base Go import-path under which to generate results")
fs.StringSliceVar(&args.PluralExceptions, "plural-exceptions", args.PluralExceptions,
"list of comma separated plural exception definitions in Type:PluralizedType format")
fs.StringVar(&args.GoHeaderFile, "go-header-file", "",
"the path to a file containing boilerplate header text; the string \"YEAR\" will be replaced with the current 4-digit year")
}
// Validate checks the given arguments.
func Validate(genericArgs *args.GeneratorArgs) error {
_ = genericArgs.CustomArgs.(*CustomArgs)
if len(genericArgs.OutputPackagePath) == 0 {
return fmt.Errorf("output package cannot be empty")
func (args *Args) Validate() error {
if len(args.OutputDir) == 0 {
return fmt.Errorf("--output-dir must be specified")
}
if len(args.OutputPkg) == 0 {
return fmt.Errorf("--output-pkg must be specified")
}
return nil
}

View File

@@ -22,17 +22,18 @@ import (
"path/filepath"
"strings"
"k8s.io/gengo/generator"
"k8s.io/gengo/types"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/types"
"k8s.io/klog/v2"
"k8s.io/code-generator/cmd/client-gen/generators/util"
)
// expansionGenerator produces a file for a expansion interfaces.
type expansionGenerator struct {
generator.DefaultGen
packagePath string
types []*types.Type
generator.GoGenerator
outputPath string
types []*types.Type
}
// We only want to call GenerateType() once per group.
@@ -44,11 +45,16 @@ func (g *expansionGenerator) GenerateType(c *generator.Context, t *types.Type, w
sw := generator.NewSnippetWriter(w, c, "$", "$")
for _, t := range g.types {
tags := util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
if _, err := os.Stat(filepath.Join(g.packagePath, strings.ToLower(t.Name.Name+"_expansion.go"))); os.IsNotExist(err) {
manualFile := filepath.Join(g.outputPath, strings.ToLower(t.Name.Name+"_expansion.go"))
if _, err := os.Stat(manualFile); err == nil {
klog.V(4).Infof("file %q exists, not generating", manualFile)
} else if os.IsNotExist(err) {
sw.Do(expansionInterfaceTemplate, t)
if !tags.NonNamespaced {
sw.Do(namespacedExpansionInterfaceTemplate, t)
}
} else {
return err
}
}
return sw.Error()

View File

@@ -19,17 +19,17 @@ package generators
import (
"fmt"
"io"
"path"
"path/filepath"
"strings"
"k8s.io/gengo/args"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"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"
"k8s.io/gengo/v2"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/namer"
"k8s.io/gengo/v2/types"
"k8s.io/klog/v2"
)
@@ -59,16 +59,16 @@ func DefaultNameSystem() string {
return "public"
}
// Packages makes the client package definition.
func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
boilerplate, err := arguments.LoadGoBoilerplate()
// GetTargets makes the client target definition.
func GetTargets(context *generator.Context, args *args.Args) []generator.Target {
boilerplate, err := gengo.GoBoilerplate(args.GoHeaderFile, "", gengo.StdGeneratedBy)
if err != nil {
klog.Fatalf("Failed loading boilerplate: %v", err)
}
var packageList generator.Packages
for _, inputDir := range arguments.InputDirs {
p := context.Universe.Package(inputDir)
var targetList []generator.Target
for _, inputPkg := range context.Inputs {
p := context.Universe.Package(inputPkg)
objectMeta, internal, err := objectMetaForPackage(p)
if err != nil {
@@ -101,7 +101,7 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
// 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 := types.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil {
if override := gengo.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil {
gv.Group = clientgentypes.Group(strings.SplitN(override[0], ".", 2)[0])
}
@@ -119,26 +119,33 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
orderer := namer.Orderer{Namer: namer.NewPrivateNamer(0)}
typesToGenerate = orderer.OrderTypes(typesToGenerate)
packagePath := filepath.Join(arguments.OutputPackagePath, groupPackageName, strings.ToLower(gv.Version.NonEmpty()))
packageList = append(packageList, &generator.DefaultPackage{
PackageName: strings.ToLower(gv.Version.NonEmpty()),
PackagePath: packagePath,
HeaderText: boilerplate,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
subdir := []string{groupPackageName, strings.ToLower(gv.Version.NonEmpty())}
outputDir := filepath.Join(args.OutputDir, filepath.Join(subdir...))
outputPkg := path.Join(args.OutputPkg, path.Join(subdir...))
targetList = append(targetList, &generator.SimpleTarget{
PkgName: strings.ToLower(gv.Version.NonEmpty()),
PkgPath: outputPkg,
PkgDir: outputDir,
HeaderComment: boilerplate,
FilterFunc: func(c *generator.Context, t *types.Type) bool {
tags := util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
return tags.GenerateClient && tags.HasVerb("list") && tags.HasVerb("get")
},
GeneratorsFunc: func(c *generator.Context) (generators []generator.Generator) {
generators = append(generators, &expansionGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: "expansion_generated",
GoGenerator: generator.GoGenerator{
OutputFilename: "expansion_generated.go",
},
packagePath: filepath.Join(arguments.OutputBase, packagePath),
types: typesToGenerate,
outputPath: outputDir,
types: typesToGenerate,
})
for _, t := range typesToGenerate {
generators = append(generators, &listerGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: strings.ToLower(t.Name.Name),
GoGenerator: generator.GoGenerator{
OutputFilename: strings.ToLower(t.Name.Name) + ".go",
},
outputPackage: arguments.OutputPackagePath,
outputPackage: outputPkg,
groupVersion: gv,
internalGVPkg: internalGVPkg,
typeToGenerate: t,
@@ -148,14 +155,10 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
}
return generators
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
tags := util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
return tags.GenerateClient && tags.HasVerb("list") && tags.HasVerb("get")
},
})
}
return packageList
return targetList
}
// objectMetaForPackage returns the type of ObjectMeta used by package p.
@@ -187,7 +190,7 @@ func isInternal(m types.Member) bool {
// listerGenerator produces a file of listers for a given GroupVersion and
// type.
type listerGenerator struct {
generator.DefaultGen
generator.GoGenerator
outputPackage string
groupVersion clientgentypes.GroupVersion
internalGVPkg string
@@ -212,6 +215,7 @@ func (g *listerGenerator) Imports(c *generator.Context) (imports []string) {
imports = append(imports, g.imports.ImportLines()...)
imports = append(imports, "k8s.io/apimachinery/pkg/api/errors")
imports = append(imports, "k8s.io/apimachinery/pkg/labels")
imports = append(imports, "k8s.io/client-go/listers")
// for Indexer
imports = append(imports, "k8s.io/client-go/tools/cache")
return
@@ -233,25 +237,21 @@ func (g *listerGenerator) GenerateType(c *generator.Context, t *types.Type, w io
}
if tags.NonNamespaced {
sw.Do(typeListerInterface_NonNamespaced, m)
sw.Do(typeListerInterfaceNonNamespaced, m)
} else {
sw.Do(typeListerInterface, m)
}
sw.Do(typeListerStruct, m)
sw.Do(typeListerConstructor, m)
sw.Do(typeLister_List, m)
if tags.NonNamespaced {
sw.Do(typeLister_NonNamespacedGet, m)
return sw.Error()
}
sw.Do(typeLister_NamespaceLister, m)
sw.Do(typeListerNamespaceLister, m)
sw.Do(namespaceListerInterface, m)
sw.Do(namespaceListerStruct, m)
sw.Do(namespaceLister_List, m)
sw.Do(namespaceLister_Get, m)
return sw.Error()
}
@@ -269,7 +269,7 @@ type $.type|public$Lister interface {
}
`
var typeListerInterface_NonNamespaced = `
var typeListerInterfaceNonNamespaced = `
// $.type|public$Lister helps list $.type|publicPlural$.
// All objects returned here must be treated as read-only.
type $.type|public$Lister interface {
@@ -283,48 +283,27 @@ type $.type|public$Lister interface {
}
`
// This embeds a typed resource indexer instead of aliasing, so that the struct
// is available as a receiver for methods specific to the generated type
// (from the corresponding expansion interface).
var typeListerStruct = `
// $.type|private$Lister implements the $.type|public$Lister interface.
type $.type|private$Lister struct {
indexer cache.Indexer
listers.ResourceIndexer[*$.type|raw$]
}
`
var typeListerConstructor = `
// New$.type|public$Lister returns a new $.type|public$Lister.
func New$.type|public$Lister(indexer cache.Indexer) $.type|public$Lister {
return &$.type|private$Lister{indexer: indexer}
return &$.type|private$Lister{listers.New[*$.type|raw$](indexer, $.Resource|raw$("$.type|lowercaseSingular$"))}
}
`
var typeLister_List = `
// List lists all $.type|publicPlural$ in the indexer.
func (s *$.type|private$Lister) List(selector labels.Selector) (ret []*$.type|raw$, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*$.type|raw$))
})
return ret, err
}
`
var typeLister_NamespaceLister = `
var typeListerNamespaceLister = `
// $.type|publicPlural$ returns an object that can list and get $.type|publicPlural$.
func (s *$.type|private$Lister) $.type|publicPlural$(namespace string) $.type|public$NamespaceLister {
return $.type|private$NamespaceLister{indexer: s.indexer, namespace: namespace}
}
`
var typeLister_NonNamespacedGet = `
// Get retrieves the $.type|public$ from the index for a given name.
func (s *$.type|private$Lister) Get(name string) (*$.type|raw$, error) {
obj, exists, err := s.indexer.GetByKey(name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound($.Resource|raw$("$.type|lowercaseSingular$"), name)
}
return obj.(*$.type|raw$), nil
return $.type|private$NamespaceLister{listers.NewNamespaced[*$.type|raw$](s.ResourceIndexer, namespace)}
}
`
@@ -342,35 +321,13 @@ type $.type|public$NamespaceLister interface {
}
`
// This embeds a typed namespaced resource indexer instead of aliasing, so that the struct
// is available as a receiver for methods specific to the generated type
// (from the corresponding expansion interface).
var namespaceListerStruct = `
// $.type|private$NamespaceLister implements the $.type|public$NamespaceLister
// interface.
type $.type|private$NamespaceLister struct {
indexer cache.Indexer
namespace string
}
`
var namespaceLister_List = `
// List lists all $.type|publicPlural$ in the indexer for a given namespace.
func (s $.type|private$NamespaceLister) List(selector labels.Selector) (ret []*$.type|raw$, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*$.type|raw$))
})
return ret, err
}
`
var namespaceLister_Get = `
// Get retrieves the $.type|public$ from the indexer for a given namespace and name.
func (s $.type|private$NamespaceLister) Get(name string) (*$.type|raw$, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound($.Resource|raw$("$.type|lowercaseSingular$"), name)
}
return obj.(*$.type|raw$), nil
listers.ResourceIndexer[*$.type|raw$]
}
`

View File

@@ -20,36 +20,38 @@ import (
"flag"
"github.com/spf13/pflag"
"k8s.io/code-generator/cmd/lister-gen/args"
"k8s.io/code-generator/cmd/lister-gen/generators"
"k8s.io/code-generator/pkg/util"
"k8s.io/gengo/v2"
"k8s.io/gengo/v2/generator"
"k8s.io/klog/v2"
generatorargs "k8s.io/code-generator/cmd/lister-gen/args"
)
func main() {
klog.InitFlags(nil)
genericArgs, customArgs := generatorargs.NewDefaults()
args := args.New()
// Override defaults.
// TODO: move this out of lister-gen
genericArgs.OutputPackagePath = "k8s.io/kubernetes/pkg/client/listers"
genericArgs.AddFlags(pflag.CommandLine)
customArgs.AddFlags(pflag.CommandLine)
args.AddFlags(pflag.CommandLine)
flag.Set("logtostderr", "true")
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
pflag.Parse()
if err := generatorargs.Validate(genericArgs); err != nil {
if err := args.Validate(); err != nil {
klog.Fatalf("Error: %v", err)
}
myTargets := func(context *generator.Context) []generator.Target {
return generators.GetTargets(context, args)
}
// Run it.
if err := genericArgs.Execute(
generators.NameSystems(util.PluralExceptionListToMapOrDie(customArgs.PluralExceptions)),
if err := gengo.Execute(
generators.NameSystems(util.PluralExceptionListToMapOrDie(args.PluralExceptions)),
generators.DefaultNameSystem(),
generators.Packages,
myTargets,
gengo.StdBuildTag,
pflag.Args(),
); err != nil {
klog.Fatalf("Error: %v", err)
}