Merge pull request #19267 from caesarxuchao/options-client-gen

Auto commit by PR queue bot
pull/6/head
k8s-merge-robot 2016-01-12 14:23:21 -08:00
commit 6d367480f9
9 changed files with 465 additions and 26 deletions

View File

@ -45,7 +45,7 @@ func Default() *GeneratorArgs {
return generatorArgs return generatorArgs
} }
// GeneratorArgs has arguments common to most generators. // GeneratorArgs has arguments that are passed to generators.
type GeneratorArgs struct { type GeneratorArgs struct {
// Which directories to parse. // Which directories to parse.
InputDirs []string InputDirs []string
@ -61,6 +61,9 @@ type GeneratorArgs struct {
// If true, only verify, don't write anything. // If true, only verify, don't write anything.
VerifyOnly bool VerifyOnly bool
// Any custom arguments go here
CustomArgs interface{}
} }
func (g *GeneratorArgs) AddFlags(fs *pflag.FlagSet) { func (g *GeneratorArgs) AddFlags(fs *pflag.FlagSet) {

View File

@ -26,10 +26,29 @@ import (
"k8s.io/kubernetes/cmd/libs/go2idl/generator" "k8s.io/kubernetes/cmd/libs/go2idl/generator"
"k8s.io/kubernetes/cmd/libs/go2idl/namer" "k8s.io/kubernetes/cmd/libs/go2idl/namer"
"k8s.io/kubernetes/cmd/libs/go2idl/types" "k8s.io/kubernetes/cmd/libs/go2idl/types"
"k8s.io/kubernetes/pkg/api/unversioned"
"github.com/golang/glog" "github.com/golang/glog"
) )
// ClientGenArgs is a wrapper for arguments to client-gen.
type ClientGenArgs struct {
// TODO: we should make another type declaration of GroupVersion out of the
// unversioned package, which is part of our API. Tools like client-gen
// shouldn't depend on an API.
GroupVersions []unversioned.GroupVersion
// ClientsetName is the name of the clientset to be generated. It's
// populated from command-line arguments.
ClientsetName string
// ClientsetOutputPath is the path the clientset will be generated at. It's
// populated from command-line arguments.
ClientsetOutputPath string
// ClientsetOnly determines if we should generate the clients for groups and
// types along with the clientset. It's populated from command-line
// arguments.
ClientsetOnly bool
}
// NameSystems returns the name system used by the generators in this package. // NameSystems returns the name system used by the generators in this package.
func NameSystems() namer.NameSystems { func NameSystems() namer.NameSystems {
pluralExceptions := map[string]string{ pluralExceptions := map[string]string{
@ -110,6 +129,33 @@ func packageForGroup(group string, version string, typeList []*types.Type, packa
} }
} }
func packageForClientset(customArgs ClientGenArgs, typedClientBasePath string, boilerplate []byte) generator.Package {
return &generator.DefaultPackage{
PackageName: customArgs.ClientsetName,
PackagePath: filepath.Join(customArgs.ClientsetOutputPath, customArgs.ClientsetName),
HeaderText: boilerplate,
PackageDocumentation: []byte(
`// This package has the automatically generated clientset.
`),
// GeneratorFunc returns a list of generators. Each generator generates a
// single file.
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
generators = []generator.Generator{
&genClientset{
DefaultGen: generator.DefaultGen{
OptionalName: "clientset",
},
groupVersions: customArgs.GroupVersions,
typedClientPath: typedClientBasePath,
outputPackage: customArgs.ClientsetName,
imports: generator.NewImportTracker(),
},
}
return generators
},
}
}
// Packages makes the client package definition. // Packages makes the client package definition.
func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages { func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
boilerplate, err := arguments.LoadGoBoilerplate() boilerplate, err := arguments.LoadGoBoilerplate()
@ -136,11 +182,20 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
} }
} }
customArgs, ok := arguments.CustomArgs.(ClientGenArgs)
if !ok {
glog.Fatalf("cannot convert arguments.CustomArgs to ClientGenArgs")
}
var packageList []generator.Package var packageList []generator.Package
// If --clientset-only=true, we don't regenerate the individual typed clients.
if !customArgs.ClientsetOnly {
orderer := namer.Orderer{namer.NewPrivateNamer(0)} orderer := namer.Orderer{namer.NewPrivateNamer(0)}
for group, types := range groupToTypes { for group, types := range groupToTypes {
packageList = append(packageList, packageForGroup(group, "unversioned", orderer.OrderTypes(types), arguments.OutputPackagePath, arguments.OutputBase, boilerplate)) packageList = append(packageList, packageForGroup(group, "unversioned", orderer.OrderTypes(types), arguments.OutputPackagePath, arguments.OutputBase, boilerplate))
} }
}
packageList = append(packageList, packageForClientset(customArgs, arguments.OutputPackagePath, boilerplate))
return generator.Packages(packageList) return generator.Packages(packageList)
} }

View File

@ -0,0 +1,174 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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/filepath"
"k8s.io/kubernetes/cmd/libs/go2idl/generator"
"k8s.io/kubernetes/cmd/libs/go2idl/namer"
"k8s.io/kubernetes/cmd/libs/go2idl/types"
"k8s.io/kubernetes/pkg/api/unversioned"
)
// genClientset generates a package for a clientset.
type genClientset struct {
generator.DefaultGen
groupVersions []unversioned.GroupVersion
typedClientPath string
outputPackage string
imports *generator.ImportTracker
}
var _ generator.Generator = &genClientset{}
func (g *genClientset) Namers(c *generator.Context) namer.NameSystems {
return namer.NameSystems{
"raw": namer.NewRawNamer(g.outputPackage, g.imports),
}
}
var generate_clientset = true
// We only want to call GenerateType() once.
func (g *genClientset) Filter(c *generator.Context, t *types.Type) bool {
ret := generate_clientset
generate_clientset = false
return ret
}
func normalizeGroup(group string) string {
if group == "api" {
return "legacy"
}
return group
}
func normalizeVersion(version string) string {
if version == "" {
return "unversioned"
}
return version
}
func (g *genClientset) Imports(c *generator.Context) (imports []string) {
for _, gv := range g.groupVersions {
group := normalizeGroup(gv.Group)
version := normalizeVersion(gv.Version)
typedClientPath := filepath.Join(g.typedClientPath, group, version)
imports = append(imports, g.imports.ImportLines()...)
imports = append(imports, fmt.Sprintf("%s_%s \"%s\"", group, version, typedClientPath))
}
return
}
func (g *genClientset) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
// 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, "$", "$")
const pkgUnversioned = "k8s.io/kubernetes/pkg/client/unversioned"
type arg struct {
Group string
PackageName string
}
allGroups := []arg{}
for _, gv := range g.groupVersions {
group := normalizeGroup(gv.Group)
version := normalizeVersion(gv.Version)
allGroups = append(allGroups, arg{namer.IC(group), group + "_" + version})
}
m := map[string]interface{}{
"allGroups": allGroups,
"Config": c.Universe.Type(types.Name{Package: pkgUnversioned, Name: "Config"}),
"DefaultKubernetesUserAgent": c.Universe.Function(types.Name{Package: pkgUnversioned, Name: "DefaultKubernetesUserAgent"}),
"RESTClient": c.Universe.Type(types.Name{Package: pkgUnversioned, Name: "RESTClient"}),
}
sw.Do(clientsetInterfaceTemplate, m)
sw.Do(clientsetTemplate, m)
for _, g := range allGroups {
sw.Do(clientsetInterfaceImplTemplate, g)
}
sw.Do(newClientsetForConfigTemplate, m)
sw.Do(newClientsetForConfigOrDieTemplate, m)
sw.Do(newClientsetForRESTClientTemplate, m)
return sw.Error()
}
var clientsetInterfaceTemplate = `
type Interface interface {
$range .allGroups$$.Group$() $.PackageName$.$.Group$Client
$end$
}
`
var clientsetTemplate = `
// Clientset contains the clients for groups. Each group has exactly one
// version included in a Clientset.
type Clientset struct {
$range .allGroups$*$.PackageName$.$.Group$Client
$end$
}
`
var clientsetInterfaceImplTemplate = `
// $.Group$ retrieves the $.Group$Client
func (c *Clientset) $.Group$() *$.PackageName$.$.Group$Client {
return c.$.Group$Client
}
`
var newClientsetForConfigTemplate = `
// NewForConfig creates a new Clientset for the given config.
func NewForConfig(c *$.Config|raw$) (*Clientset, error) {
var clientset Clientset
var err error
$range .allGroups$ clientset.$.Group$Client, err =$.PackageName$.NewForConfig(c)
if err!=nil {
return nil, err
}
$end$
return &clientset, nil
}
`
var newClientsetForConfigOrDieTemplate = `
// NewForConfigOrDie creates a new Clientset for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *$.Config|raw$) *Clientset {
var clientset Clientset
$range .allGroups$ clientset.$.Group$Client =$.PackageName$.NewForConfigOrDie(c)
$end$
return &clientset
}
`
var newClientsetForRESTClientTemplate = `
// New creates a new Clientset for the given RESTClient.
func New(c *$.RESTClient|raw$) *Clientset {
var clientset Clientset
$range .allGroups$ clientset.$.Group$Client =$.PackageName$.New(c)
$end$
return &clientset
}
`

View File

@ -34,6 +34,8 @@ type genGroup struct {
imports *generator.ImportTracker imports *generator.ImportTracker
} }
var _ generator.Generator = &genGroup{}
// We only want to call GenerateType() once per group. // We only want to call GenerateType() once per group.
func (g *genGroup) Filter(c *generator.Context, t *types.Type) bool { func (g *genGroup) Filter(c *generator.Context, t *types.Type) bool {
return t == g.types[0] return t == g.types[0]

View File

@ -34,6 +34,8 @@ type genClientForType struct {
imports *generator.ImportTracker imports *generator.ImportTracker
} }
var _ generator.Generator = &genClientForType{}
// Filter ignores all but one type because we're making a single file per type. // 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 } func (g *genClientForType) Filter(c *generator.Context, t *types.Type) bool { return t == g.typeToMatch }

View File

@ -18,45 +18,99 @@ limitations under the License.
package main package main
import ( import (
"fmt"
"path/filepath"
"k8s.io/kubernetes/cmd/libs/go2idl/args" "k8s.io/kubernetes/cmd/libs/go2idl/args"
"k8s.io/kubernetes/cmd/libs/go2idl/client-gen/generators" "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/generators"
"k8s.io/kubernetes/pkg/api/unversioned"
"github.com/golang/glog" "github.com/golang/glog"
flag "github.com/spf13/pflag" flag "github.com/spf13/pflag"
) )
var test = flag.BoolP("test", "t", false, "set this flag to generate the client code for the testdata") var (
test = flag.BoolP("test", "t", false, "set this flag to generate the client code for the testdata")
inputVersions = flag.StringSlice("input", []string{"api/", "extensions/"}, "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...\". Default to \"api/,extensions\"")
clientsetName = flag.StringP("clientset-name", "n", "release_1_1", "the name of the generated clientset package.")
clientsetPath = flag.String("clientset-path", "k8s.io/kubernetes/pkg/client/clientset_generated/", "the generated clientset will be output to <clientset-path>/<clientset-name>. Default to \"k8s.io/kubernetes/pkg/client/clientset_generated/\"")
clientsetOnly = flag.Bool("clientset-only", false, "when set, client-gen only generates the clientset shell, without generating the individual typed clients")
)
func versionToPath(group string, version string) (path string) {
const base = "k8s.io/kubernetes/pkg"
// special case for the legacy group
if group == "api" {
path = filepath.Join(base, "api", version)
} else {
path = filepath.Join(base, "apis", group, version)
}
return
}
func parseInputVersions() ([]string, []unversioned.GroupVersion, error) {
var visitedGroups = make(map[string]struct{})
var groupVersions []unversioned.GroupVersion
var paths []string
for _, gvString := range *inputVersions {
gv, err := unversioned.ParseGroupVersion(gvString)
if err != nil {
return nil, nil, err
}
if _, found := visitedGroups[gv.Group]; found {
return nil, nil, fmt.Errorf("group %q appeared more than once in the input. At most one version is allowed for each group.", gv.Group)
}
visitedGroups[gv.Group] = struct{}{}
groupVersions = append(groupVersions, gv)
paths = append(paths, versionToPath(gv.Group, gv.Version))
}
return paths, groupVersions, nil
}
func main() { func main() {
arguments := args.Default() arguments := args.Default()
flag.Parse() flag.Parse()
if *test { dependencies := []string{
// Override defaults. These are Kubernetes specific input and output
// locations.
arguments.InputDirs = []string{
"k8s.io/kubernetes/cmd/libs/go2idl/client-gen/testdata/apis/testgroup",
"k8s.io/kubernetes/pkg/fields", "k8s.io/kubernetes/pkg/fields",
"k8s.io/kubernetes/pkg/labels", "k8s.io/kubernetes/pkg/labels",
"k8s.io/kubernetes/pkg/watch", "k8s.io/kubernetes/pkg/watch",
"k8s.io/kubernetes/pkg/client/unversioned", "k8s.io/kubernetes/pkg/client/unversioned",
"k8s.io/kubernetes/pkg/api/latest", "k8s.io/kubernetes/pkg/api/latest",
} }
if *test {
arguments.InputDirs = append(dependencies, []string{
"k8s.io/kubernetes/cmd/libs/go2idl/client-gen/testdata/apis/testgroup",
}...)
// We may change the output path later. // We may change the output path later.
arguments.OutputPackagePath = "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/testoutput" arguments.OutputPackagePath = "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/testoutput"
} else { arguments.CustomArgs = generators.ClientGenArgs{
// Override defaults. These are Kubernetes specific input and output []unversioned.GroupVersion{{"testgroup", ""}},
// locations. "test_release_1_1",
arguments.InputDirs = []string{ "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/testoutput/clientset_generated/",
"k8s.io/kubernetes/pkg/api", false,
"k8s.io/kubernetes/pkg/apis/extensions",
"k8s.io/kubernetes/pkg/fields",
"k8s.io/kubernetes/pkg/labels",
"k8s.io/kubernetes/pkg/watch",
"k8s.io/kubernetes/pkg/client/unversioned",
"k8s.io/kubernetes/pkg/api/latest",
} }
} else {
inputPath, groupVersions, err := parseInputVersions()
if err != nil {
glog.Fatalf("Error: %v", err)
}
glog.Info("going to generate clientset from these input paths: %v", inputPath)
arguments.InputDirs = append(inputPath, dependencies...)
// TODO: we need to make OutPackagePath a map[string]string. For example,
// we need clientset and the individual typed clients be output to different
// output path.
// We may change the output path later. // We may change the output path later.
arguments.OutputPackagePath = "k8s.io/kubernetes/pkg/client/typed/generated" arguments.OutputPackagePath = "k8s.io/kubernetes/pkg/client/typed/generated"
arguments.CustomArgs = generators.ClientGenArgs{
groupVersions,
*clientsetName,
*clientsetPath,
*clientsetOnly,
}
} }
if err := arguments.Execute( if err := arguments.Execute(

View File

@ -0,0 +1,66 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 test_release_1_1
import (
testgroup_unversioned "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned"
unversioned "k8s.io/kubernetes/pkg/client/unversioned"
)
type Interface interface {
Testgroup() testgroup_unversioned.TestgroupClient
}
// Clientset contains the clients for groups. Each group has exactly one
// version included in a Clientset.
type Clientset struct {
*testgroup_unversioned.TestgroupClient
}
// Testgroup retrieves the TestgroupClient
func (c *Clientset) Testgroup() *testgroup_unversioned.TestgroupClient {
return c.TestgroupClient
}
// NewForConfig creates a new Clientset for the given config.
func NewForConfig(c *unversioned.Config) (*Clientset, error) {
var clientset Clientset
var err error
clientset.TestgroupClient, err = testgroup_unversioned.NewForConfig(c)
if err != nil {
return nil, err
}
return &clientset, nil
}
// NewForConfigOrDie creates a new Clientset for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *unversioned.Config) *Clientset {
var clientset Clientset
clientset.TestgroupClient = testgroup_unversioned.NewForConfigOrDie(c)
return &clientset
}
// New creates a new Clientset for the given RESTClient.
func New(c *unversioned.RESTClient) *Clientset {
var clientset Clientset
clientset.TestgroupClient = testgroup_unversioned.New(c)
return &clientset
}

View File

@ -354,3 +354,6 @@ watch-only
whitelist-override-label whitelist-override-label
windows-line-endings windows-line-endings
www-prefix www-prefix
clientset-name
clientset-only
clientset-path

View File

@ -0,0 +1,80 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 release_1_1
import (
extensions_unversioned "k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned"
legacy_unversioned "k8s.io/kubernetes/pkg/client/typed/generated/legacy/unversioned"
unversioned "k8s.io/kubernetes/pkg/client/unversioned"
)
type Interface interface {
Legacy() legacy_unversioned.LegacyClient
Extensions() extensions_unversioned.ExtensionsClient
}
// Clientset contains the clients for groups. Each group has exactly one
// version included in a Clientset.
type Clientset struct {
*legacy_unversioned.LegacyClient
*extensions_unversioned.ExtensionsClient
}
// Legacy retrieves the LegacyClient
func (c *Clientset) Legacy() *legacy_unversioned.LegacyClient {
return c.LegacyClient
}
// Extensions retrieves the ExtensionsClient
func (c *Clientset) Extensions() *extensions_unversioned.ExtensionsClient {
return c.ExtensionsClient
}
// NewForConfig creates a new Clientset for the given config.
func NewForConfig(c *unversioned.Config) (*Clientset, error) {
var clientset Clientset
var err error
clientset.LegacyClient, err = legacy_unversioned.NewForConfig(c)
if err != nil {
return nil, err
}
clientset.ExtensionsClient, err = extensions_unversioned.NewForConfig(c)
if err != nil {
return nil, err
}
return &clientset, nil
}
// NewForConfigOrDie creates a new Clientset for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *unversioned.Config) *Clientset {
var clientset Clientset
clientset.LegacyClient = legacy_unversioned.NewForConfigOrDie(c)
clientset.ExtensionsClient = extensions_unversioned.NewForConfigOrDie(c)
return &clientset
}
// New creates a new Clientset for the given RESTClient.
func New(c *unversioned.RESTClient) *Clientset {
var clientset Clientset
clientset.LegacyClient = legacy_unversioned.New(c)
clientset.ExtensionsClient = extensions_unversioned.New(c)
return &clientset
}