client-gen: stratify main.go

Remove double flag parsing and prepare for being instantiated inside a multi-generator process.
This commit is contained in:
Dr. Stefan Schimanski 2017-09-28 11:14:32 +02:00
parent c1062de2ff
commit a65232008d
7 changed files with 456 additions and 204 deletions

View File

@ -16,16 +16,23 @@ limitations under the License.
package args
import "k8s.io/code-generator/cmd/client-gen/types"
import (
"github.com/spf13/pflag"
"k8s.io/code-generator/cmd/client-gen/types"
)
// ClientGenArgs is a wrapper for arguments to client-gen.
type Args struct {
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
// GroupVersionToInputPath is a map between GroupVersion and the path to
// the respective types.go. We still need GroupVersions in the struct because
// we need an order.
// GroupVersionToInputPath is a map between GroupVersion and the path to the respective
// types.go, relative to InputBasePath. We still need GroupVersions in the
// struct because we need an order.
GroupVersionToInputPath map[types.GroupVersion]string
// The base for the path of GroupVersionToInputPath.
InputBasePath string
// Overrides for which types should be included in the client.
IncludedTypesOverrides map[types.GroupVersion][]string
@ -44,6 +51,33 @@ type Args struct {
ClientsetOnly bool
// FakeClient determines if client-gen generates the fake clients.
FakeClient bool
// CmdArgs is the command line arguments supplied when the client-gen is called.
CmdArgs string
}
var defaultInput = []string{
"api/",
"admissionregistration/",
"authentication/",
"authorization/",
"autoscaling/",
"batch/",
"certificates/",
"extensions/",
"rbac/",
"storage/",
"apps/",
"policy/",
"scheduling/",
"settings/",
"networking/",
}
func (ca *CustomArgs) AddFlags(fs *pflag.FlagSet) {
pflag.Var(NewGVPackagesValue(&ca.GroupVersionToInputPath, &ca.Groups, defaultInput), "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.StringVar(&ca.InputBasePath, "input-base", "k8s.io/kubernetes/pkg/apis", "base path to look for the api group.")
pflag.StringVarP(&ca.ClientsetName, "clientset-name", "n", "internalclientset", "the name of the generated clientset package.")
pflag.StringVarP(&ca.ClientsetAPIPath, "clientset-api-path", "", "", "the value of default API path.")
pflag.StringVar(&ca.ClientsetOutputPath, "clientset-path", "k8s.io/kubernetes/pkg/client/clientset_generated/", "the generated clientset will be output to <clientset-path>/<clientset-name>.")
pflag.BoolVar(&ca.ClientsetOnly, "clientset-only", false, "when set, client-gen only generates the clientset shell, without generating the individual typed clients")
pflag.BoolVar(&ca.FakeClient, "fake-clientset", true, "when set, client-gen will generate the fake clientset that can be used in tests")
}

View File

@ -0,0 +1,159 @@
/*
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 args
import (
"bytes"
"encoding/csv"
"flag"
"path"
"strings"
"path/filepath"
"sort"
"k8s.io/code-generator/cmd/client-gen/types"
)
type gvPackagesValue struct {
gvToPath *map[types.GroupVersion]string
groups *[]types.GroupVersions
changed bool
}
func NewGVPackagesValue(gvToPath *map[types.GroupVersion]string, groups *[]types.GroupVersions, def []string) *gvPackagesValue {
gvp := new(gvPackagesValue)
gvp.gvToPath = gvToPath
gvp.groups = groups
if def != nil {
if err := gvp.set(def); err != nil {
panic(err)
}
}
return gvp
}
var _ flag.Value = &gvPackagesValue{}
func readAsCSV(val string) ([]string, error) {
if val == "" {
return []string{}, nil
}
stringReader := strings.NewReader(val)
csvReader := csv.NewReader(stringReader)
return csvReader.Read()
}
func writeAsCSV(vals []string) (string, error) {
b := &bytes.Buffer{}
w := csv.NewWriter(b)
err := w.Write(vals)
if err != nil {
return "", err
}
w.Flush()
return strings.TrimSuffix(b.String(), "\n"), nil
}
func (s *gvPackagesValue) set(vs []string) error {
if !s.changed {
*s.gvToPath = map[types.GroupVersion]string{}
*s.groups = []types.GroupVersions{}
}
var seenGroups = make(map[types.Group]*types.GroupVersions)
for _, g := range *s.groups {
seenGroups[g.Group] = &g
}
for _, v := range vs {
pth, gvString := parsePathGroupVersion(v)
gv, err := types.ToGroupVersion(gvString)
if err != nil {
return err
}
if group, ok := seenGroups[gv.Group]; ok {
seenGroups[gv.Group].Versions = append(group.Versions, gv.Version)
} else {
seenGroups[gv.Group] = &types.GroupVersions{
Group: gv.Group,
Versions: []types.Version{gv.Version},
}
}
(*s.gvToPath)[gv] = groupVersionPath(pth, gv.Group.String(), gv.Version.String())
}
var groupNames []string
for groupName := range seenGroups {
groupNames = append(groupNames, groupName.String())
}
sort.Strings(groupNames)
*s.groups = []types.GroupVersions{}
for _, groupName := range groupNames {
*s.groups = append(*s.groups, *seenGroups[types.Group(groupName)])
}
return nil
}
func (s *gvPackagesValue) Set(val string) error {
vs, err := readAsCSV(val)
if err != nil {
return err
}
if err := s.set(vs); err != nil {
return err
}
s.changed = true
return nil
}
func (s *gvPackagesValue) Type() string {
return "stringSlice"
}
func (s *gvPackagesValue) String() string {
strs := make([]string, 0, len(*s.gvToPath))
for gv, pth := range *s.gvToPath {
strs = append(strs, path.Join(pth, gv.Group.String(), gv.Version.String()))
}
str, _ := writeAsCSV(strs)
return "[" + str + "]"
}
func parsePathGroupVersion(pgvString string) (gvPath string, gvString string) {
subs := strings.Split(pgvString, "/")
length := len(subs)
switch length {
case 0, 1, 2:
return "", pgvString
default:
return strings.Join(subs[:length-2], "/"), strings.Join(subs[length-2:], "/")
}
}
func groupVersionPath(gvPath string, group string, version string) (path string) {
// special case for the core group
if group == "api" {
path = filepath.Join("../api", version)
} else {
path = filepath.Join(gvPath, group, version)
}
return
}

View File

@ -0,0 +1,109 @@
/*
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 args
import (
"fmt"
"reflect"
"strings"
"testing"
"github.com/spf13/pflag"
"k8s.io/code-generator/cmd/client-gen/types"
)
func TestGVPackageFlag(t *testing.T) {
tests := []struct {
args []string
def []string
expected map[types.GroupVersion]string
expectedGroups []types.GroupVersions
parseError string
}{
{
args: []string{},
expected: map[types.GroupVersion]string{},
expectedGroups: []types.GroupVersions{},
},
{
args: []string{"foo/bar/v1", "foo/bar/v2", "foo/bar/", "foo/v1"},
expected: map[types.GroupVersion]string{
{Group: "bar", Version: ""}: "foo/bar",
{Group: "bar", Version: "v1"}: "foo/bar/v1",
{Group: "bar", Version: "v2"}: "foo/bar/v2",
{Group: "foo", Version: "v1"}: "foo/v1",
},
expectedGroups: []types.GroupVersions{
{Group: types.Group("bar"), Versions: []types.Version{types.Version("v1"), types.Version("v2"), types.Version("")}},
{Group: types.Group("foo"), Versions: []types.Version{types.Version("v1")}},
},
},
{
args: []string{"foo/bar/v1", "foo/bar/v2", "foo/bar/", "foo/v1"},
def: []string{"foo/bar/v1alpha1", "foo/v1"},
expected: map[types.GroupVersion]string{
{Group: "bar", Version: ""}: "foo/bar",
{Group: "bar", Version: "v1"}: "foo/bar/v1",
{Group: "bar", Version: "v2"}: "foo/bar/v2",
{Group: "foo", Version: "v1"}: "foo/v1",
},
expectedGroups: []types.GroupVersions{
{Group: types.Group("bar"), Versions: []types.Version{types.Version("v1"), types.Version("v2"), types.Version("")}},
{Group: types.Group("foo"), Versions: []types.Version{types.Version("v1")}},
},
},
{
args: []string{"api/v1", "api"},
expected: map[types.GroupVersion]string{
{Group: "api", Version: "v1"}: "../api/v1",
{Group: "api", Version: ""}: "../api",
},
expectedGroups: []types.GroupVersions{
{Group: types.Group("api"), Versions: []types.Version{types.Version("v1"), types.Version("")}},
},
},
}
for i, test := range tests {
fs := pflag.NewFlagSet("testGVPackage", pflag.ContinueOnError)
gvp := map[types.GroupVersion]string{}
groups := []types.GroupVersions{}
fs.Var(NewGVPackagesValue(&gvp, &groups, test.def), "input", "usage")
args := []string{}
for _, a := range test.args {
args = append(args, fmt.Sprintf("--input=%s", a))
}
err := fs.Parse(args)
if test.parseError != "" {
if err == nil {
t.Errorf("%d: expected error %q, got nil", i, test.parseError)
} else if !strings.Contains(err.Error(), test.parseError) {
t.Errorf("%d: expected error %q, got %q", i, test.parseError, err)
}
} else if err != nil {
t.Errorf("%d: expected nil error, got %v", i, err)
}
if !reflect.DeepEqual(gvp, test.expected) {
t.Errorf("%d: expected %+v, got %+v", i, test.expected, gvp)
}
if !reflect.DeepEqual(groups, test.expectedGroups) {
t.Errorf("%d: expected groups %+v, got groups %+v", i, test.expectedGroups, groups)
}
}
}

View File

@ -0,0 +1,110 @@
/*
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 args
import (
"flag"
"fmt"
"strings"
"k8s.io/code-generator/cmd/client-gen/types"
)
type gvTypeValue struct {
gvToTypes *map[types.GroupVersion][]string
changed bool
}
func NewGVTypesValue(gvToTypes *map[types.GroupVersion][]string, def []string) *gvTypeValue {
gvt := new(gvTypeValue)
gvt.gvToTypes = gvToTypes
if def != nil {
if err := gvt.set(def); err != nil {
panic(err)
}
}
return gvt
}
var _ flag.Value = &gvTypeValue{}
func (s *gvTypeValue) set(vs []string) error {
if !s.changed {
*s.gvToTypes = map[types.GroupVersion][]string{}
}
for _, input := range vs {
gvString, typeStr, err := parseGroupVersionType(input)
if err != nil {
return err
}
gv, err := types.ToGroupVersion(gvString)
if err != nil {
return err
}
types, ok := (*s.gvToTypes)[gv]
if !ok {
types = []string{}
}
types = append(types, typeStr)
(*s.gvToTypes)[gv] = types
}
return nil
}
func (s *gvTypeValue) Set(val string) error {
vs, err := readAsCSV(val)
if err != nil {
return err
}
if err := s.set(vs); err != nil {
return err
}
s.changed = true
return nil
}
func (s *gvTypeValue) Type() string {
return "stringSlice"
}
func (s *gvTypeValue) String() string {
strs := make([]string, 0, len(*s.gvToTypes))
for gv, ts := range *s.gvToTypes {
for _, t := range ts {
strs = append(strs, gv.Group.String()+"/"+gv.Version.String()+"/"+t)
}
}
str, _ := writeAsCSV(strs)
return "[" + str + "]"
}
func parseGroupVersionType(gvtString string) (gvString string, typeStr string, err error) {
invalidFormatErr := fmt.Errorf("invalid value: %s, should be of the form group/version/type", gvtString)
subs := strings.Split(gvtString, "/")
length := len(subs)
switch length {
case 2:
// gvtString of the form group/type, e.g. api/Service,extensions/ReplicaSet
return subs[0] + "/", subs[1], nil
case 3:
return strings.Join(subs[:length-1], "/"), subs[length-1], nil
default:
return "", "", invalidFormatErr
}
}

View File

@ -18,7 +18,6 @@ limitations under the License.
package generators
import (
"fmt"
"path/filepath"
"strings"
@ -104,22 +103,14 @@ func DefaultNameSystem() string {
return "public"
}
func generatedBy(customArgs *clientgenargs.Args) string {
if len(customArgs.CmdArgs) != 0 {
return fmt.Sprintf("\n// This package is generated by client-gen with custom arguments.\n\n")
}
return fmt.Sprintf("\n// This package is generated by client-gen with the default arguments.\n\n")
}
func packageForGroup(gv clientgentypes.GroupVersion, typeList []*types.Type, clientsetPackage string, apiPath string, srcTreePath string, inputPackage string, boilerplate []byte, generatedBy string) generator.Package {
func packageForGroup(gv clientgentypes.GroupVersion, typeList []*types.Type, clientsetPackage string, apiPath string, srcTreePath string, inputPackage string, boilerplate []byte) generator.Package {
groupVersionClientPackage := strings.ToLower(filepath.Join(clientsetPackage, "typed", gv.Group.NonEmpty(), gv.Version.NonEmpty()))
return &generator.DefaultPackage{
PackageName: strings.ToLower(gv.Version.NonEmpty()),
PackagePath: groupVersionClientPackage,
HeaderText: boilerplate,
PackageDocumentation: []byte(
generatedBy +
`// This package has the automatically generated typed clients.
`// This package has the automatically generated typed clients.
`),
// GeneratorFunc returns a list of generators. Each generator makes a
// single file.
@ -175,14 +166,13 @@ func packageForGroup(gv clientgentypes.GroupVersion, typeList []*types.Type, cli
}
}
func packageForClientset(customArgs *clientgenargs.Args, clientsetPackage string, boilerplate []byte, generatedBy string) generator.Package {
func packageForClientset(customArgs *clientgenargs.CustomArgs, clientsetPackage string, boilerplate []byte) generator.Package {
return &generator.DefaultPackage{
PackageName: customArgs.ClientsetName,
PackagePath: clientsetPackage,
HeaderText: boilerplate,
PackageDocumentation: []byte(
generatedBy +
`// This package has the automatically generated clientset.
`// This package has the automatically generated clientset.
`),
// GeneratorFunc returns a list of generators. Each generator generates a
// single file.
@ -206,7 +196,7 @@ func packageForClientset(customArgs *clientgenargs.Args, clientsetPackage string
}
}
func packageForScheme(customArgs *clientgenargs.Args, clientsetPackage string, srcTreePath string, boilerplate []byte, generatedBy string) generator.Package {
func packageForScheme(customArgs *clientgenargs.CustomArgs, clientsetPackage string, srcTreePath string, boilerplate []byte) generator.Package {
schemePackage := filepath.Join(clientsetPackage, "scheme")
// create runtime.Registry for internal client because it has to know about group versions
@ -226,8 +216,7 @@ NextGroup:
PackagePath: schemePackage,
HeaderText: boilerplate,
PackageDocumentation: []byte(
generatedBy +
`// This package contains the scheme of the automatically generated clientset.
`// This package contains the scheme of the automatically generated clientset.
`),
// GeneratorFunc returns a list of generators. Each generator generates a
// single file.
@ -256,7 +245,7 @@ NextGroup:
// applyGroupOverrides applies group name overrides to each package, if applicable. If there is 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.
func applyGroupOverrides(universe types.Universe, customArgs *clientgenargs.Args) {
func applyGroupOverrides(universe types.Universe, customArgs *clientgenargs.CustomArgs) {
// 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.GroupVersionToInputPath {
@ -312,16 +301,14 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
glog.Fatalf("Failed loading boilerplate: %v", err)
}
customArgs, ok := arguments.CustomArgs.(*clientgenargs.Args)
customArgs, ok := arguments.CustomArgs.(*clientgenargs.CustomArgs)
if !ok {
glog.Fatalf("cannot convert arguments.CustomArgs to clientgenargs.Args")
glog.Fatalf("cannot convert arguments.CustomArgs to clientgenargs.CustomArgs")
}
includedTypesOverrides := customArgs.IncludedTypesOverrides
applyGroupOverrides(context.Universe, customArgs)
generatedBy := generatedBy(customArgs)
gvToTypes := map[clientgentypes.GroupVersion][]*types.Type{}
for gv, inputDir := range customArgs.GroupVersionToInputPath {
// Package are indexed with the vendor prefix stripped
@ -357,10 +344,10 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
var packageList []generator.Package
clientsetPackage := filepath.Join(customArgs.ClientsetOutputPath, customArgs.ClientsetName)
packageList = append(packageList, packageForClientset(customArgs, clientsetPackage, boilerplate, generatedBy))
packageList = append(packageList, packageForScheme(customArgs, clientsetPackage, arguments.OutputBase, boilerplate, generatedBy))
packageList = append(packageList, packageForClientset(customArgs, clientsetPackage, boilerplate))
packageList = append(packageList, packageForScheme(customArgs, clientsetPackage, arguments.OutputBase, boilerplate))
if customArgs.FakeClient {
packageList = append(packageList, fake.PackageForClientset(customArgs, clientsetPackage, boilerplate, generatedBy))
packageList = append(packageList, fake.PackageForClientset(customArgs, clientsetPackage, boilerplate))
}
// If --clientset-only=true, we don't regenerate the individual typed clients.
@ -374,9 +361,9 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
gv := clientgentypes.GroupVersion{Group: group.Group, Version: version}
types := gvToTypes[gv]
inputPath := customArgs.GroupVersionToInputPath[gv]
packageList = append(packageList, packageForGroup(gv, orderer.OrderTypes(types), clientsetPackage, customArgs.ClientsetAPIPath, arguments.OutputBase, inputPath, boilerplate, generatedBy))
packageList = append(packageList, packageForGroup(gv, orderer.OrderTypes(types), clientsetPackage, customArgs.ClientsetAPIPath, arguments.OutputBase, inputPath, boilerplate))
if customArgs.FakeClient {
packageList = append(packageList, fake.PackageForGroup(gv, orderer.OrderTypes(types), clientsetPackage, inputPath, boilerplate, generatedBy))
packageList = append(packageList, fake.PackageForGroup(gv, orderer.OrderTypes(types), clientsetPackage, inputPath, boilerplate))
}
}
}

View File

@ -29,7 +29,7 @@ import (
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
)
func PackageForGroup(gv clientgentypes.GroupVersion, typeList []*types.Type, clientsetPackage string, inputPackage string, boilerplate []byte, generatedBy string) generator.Package {
func PackageForGroup(gv clientgentypes.GroupVersion, typeList []*types.Type, clientsetPackage string, inputPackage string, boilerplate []byte) generator.Package {
outputPackage := strings.ToLower(filepath.Join(clientsetPackage, "typed", gv.Group.NonEmpty(), gv.Version.NonEmpty(), "fake"))
// TODO: should make this a function, called by here and in client-generator.go
realClientPackage := filepath.Join(clientsetPackage, "typed", gv.Group.NonEmpty(), gv.Version.NonEmpty())
@ -38,8 +38,7 @@ func PackageForGroup(gv clientgentypes.GroupVersion, typeList []*types.Type, cli
PackagePath: outputPackage,
HeaderText: boilerplate,
PackageDocumentation: []byte(
generatedBy +
`// Package fake has the automatically generated clients.
`// Package fake has the automatically generated clients.
`),
// GeneratorFunc returns a list of generators. Each generator makes a
// single file.
@ -83,7 +82,7 @@ func PackageForGroup(gv clientgentypes.GroupVersion, typeList []*types.Type, cli
}
}
func PackageForClientset(customArgs *clientgenargs.Args, fakeClientsetPackage string, boilerplate []byte, generatedBy string) generator.Package {
func PackageForClientset(customArgs *clientgenargs.CustomArgs, fakeClientsetPackage string, boilerplate []byte) generator.Package {
return &generator.DefaultPackage{
// TODO: we'll generate fake clientset for different release in the future.
// Package name and path are hard coded for now.
@ -91,8 +90,7 @@ func PackageForClientset(customArgs *clientgenargs.Args, fakeClientsetPackage st
PackagePath: filepath.Join(fakeClientsetPackage, "fake"),
HeaderText: boilerplate,
PackageDocumentation: []byte(
generatedBy +
`// This package has the automatically generated fake clientset.
`// This package has the automatically generated fake clientset.
`),
// GeneratorFunc returns a list of generators. Each generator generates a
// single file.

View File

@ -19,192 +19,47 @@ package main
import (
"flag"
"fmt"
"path"
"path/filepath"
"sort"
"strings"
clientgenargs "k8s.io/code-generator/cmd/client-gen/args"
"k8s.io/code-generator/cmd/client-gen/generators"
"k8s.io/code-generator/cmd/client-gen/types"
"k8s.io/gengo/args"
"github.com/golang/glog"
"github.com/spf13/pflag"
clientgenargs "k8s.io/code-generator/cmd/client-gen/args"
"k8s.io/code-generator/cmd/client-gen/generators"
"k8s.io/gengo/args"
)
var (
inputVersions = pflag.StringSlice("input", []string{
"api/",
"admissionregistration/",
"authentication/",
"authorization/",
"autoscaling/",
"batch/",
"certificates/",
"extensions/",
"rbac/",
"storage/",
"apps/",
"policy/",
"scheduling/",
"settings/",
"networking/",
}, "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...\".")
includedTypesOverrides = pflag.StringSlice("included-types-overrides", []string{}, "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.")
basePath = pflag.String("input-base", "k8s.io/kubernetes/pkg/apis", "base path to look for the api group.")
clientsetName = pflag.StringP("clientset-name", "n", "internalclientset", "the name of the generated clientset package.")
clientsetAPIPath = pflag.StringP("clientset-api-path", "", "", "the value of default API path.")
clientsetPath = pflag.String("clientset-path", "k8s.io/kubernetes/pkg/client/clientset_generated/", "the generated clientset will be output to <clientset-path>/<clientset-name>.")
clientsetOnly = pflag.Bool("clientset-only", false, "when set, client-gen only generates the clientset shell, without generating the individual typed clients")
fakeClient = pflag.Bool("fake-clientset", true, "when set, client-gen will generate the fake clientset that can be used in tests")
)
func versionToPath(gvPath string, group string, version string) (path string) {
// special case for the core group
if group == "api" {
path = filepath.Join(*basePath, "../api", version)
} else {
path = filepath.Join(*basePath, gvPath, group, version)
}
return
}
func parseGroupVersionType(gvtString string) (gvString string, typeStr string, err error) {
invalidFormatErr := fmt.Errorf("invalid value: %s, should be of the form group/version/type", gvtString)
subs := strings.Split(gvtString, "/")
length := len(subs)
switch length {
case 2:
// gvtString of the form group/type, e.g. api/Service,extensions/ReplicaSet
return subs[0] + "/", subs[1], nil
case 3:
return strings.Join(subs[:length-1], "/"), subs[length-1], nil
default:
return "", "", invalidFormatErr
}
}
func parsePathGroupVersion(pgvString string) (gvPath string, gvString string) {
subs := strings.Split(pgvString, "/")
length := len(subs)
switch length {
case 0, 1, 2:
return "", pgvString
default:
return strings.Join(subs[:length-2], "/"), strings.Join(subs[length-2:], "/")
}
}
func parseInputVersions() (paths []string, groups []types.GroupVersions, gvToPath map[types.GroupVersion]string, err error) {
var seenGroups = make(map[types.Group]*types.GroupVersions)
gvToPath = make(map[types.GroupVersion]string)
for _, input := range *inputVersions {
gvPath, gvString := parsePathGroupVersion(input)
gv, err := types.ToGroupVersion(gvString)
if err != nil {
return nil, nil, nil, err
}
if group, ok := seenGroups[gv.Group]; ok {
(*seenGroups[gv.Group]).Versions = append(group.Versions, gv.Version)
} else {
seenGroups[gv.Group] = &types.GroupVersions{
Group: gv.Group,
Versions: []types.Version{gv.Version},
}
}
path := versionToPath(gvPath, gv.Group.String(), gv.Version.String())
paths = append(paths, path)
gvToPath[gv] = path
}
var groupNames []string
for groupName := range seenGroups {
groupNames = append(groupNames, groupName.String())
}
sort.Strings(groupNames)
for _, groupName := range groupNames {
groups = append(groups, *seenGroups[types.Group(groupName)])
}
return paths, groups, gvToPath, nil
}
func parseIncludedTypesOverrides() (map[types.GroupVersion][]string, error) {
overrides := make(map[types.GroupVersion][]string)
for _, input := range *includedTypesOverrides {
gvString, typeStr, err := parseGroupVersionType(input)
if err != nil {
return nil, err
}
gv, err := types.ToGroupVersion(gvString)
if err != nil {
return nil, err
}
types, ok := overrides[gv]
if !ok {
types = []string{}
}
types = append(types, typeStr)
overrides[gv] = types
}
return overrides, nil
}
func main() {
arguments := args.Default().WithoutDefaultFlagParsing()
dependencies := []string{
// Custom args.
customArgs := &clientgenargs.CustomArgs{}
customArgs.AddFlags(pflag.CommandLine)
// Override defaults.
arguments.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), "k8s.io/kubernetes/hack/boilerplate/boilerplate.go.txt")
arguments.CustomArgs = customArgs
arguments.InputDirs = []string{
"k8s.io/apimachinery/pkg/fields",
"k8s.io/apimachinery/pkg/labels",
"k8s.io/apimachinery/pkg/watch",
"k8s.io/apimachinery/pkg/apimachinery/registered",
}
customArgs := clientgenargs.Args{
ClientsetName: *clientsetName,
ClientsetAPIPath: *clientsetAPIPath,
ClientsetOutputPath: *clientsetPath,
ClientsetOnly: *clientsetOnly,
FakeClient: *fakeClient,
}
// Override defaults.
arguments.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), "k8s.io/kubernetes/hack/boilerplate/boilerplate.go.txt")
arguments.CustomArgs = customArgs
// Register default flags. We do this manually here because we have to override InputDirs below after additional
// input dirs are parse fromt he command-line.
arguments.AddFlags(pflag.CommandLine)
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
pflag.Parse()
// Post-parse arguments.
inputPath, groups, gvToPath, err := parseInputVersions()
if err != nil {
glog.Fatalf("Error: %v", err)
// Prefix with InputBaseDir and add client dirs as input dirs.
for gv, pth := range customArgs.GroupVersionToInputPath {
customArgs.GroupVersionToInputPath[gv] = path.Join(customArgs.InputBasePath, pth)
}
includedTypesOverrides, err := parseIncludedTypesOverrides()
if err != nil {
glog.Fatalf("Unexpected error: %v", err)
for _, pkg := range customArgs.GroupVersionToInputPath {
arguments.InputDirs = append(arguments.InputDirs, pkg)
}
glog.V(3).Infof("going to generate clientset from these input paths: %v", inputPath)
// Override
arguments.InputDirs = append(arguments.InputDirs, append(inputPath, dependencies...)...)
customArgs.Groups = groups
customArgs.GroupVersionToInputPath = gvToPath
customArgs.IncludedTypesOverrides = includedTypesOverrides
// Derive customArgs.CmdArgs
var cmdArgs string
pflag.VisitAll(func(f *pflag.Flag) {
if !f.Changed || f.Name == "verify-only" {
return
}
cmdArgs = cmdArgs + fmt.Sprintf("--%s=%s ", f.Name, f.Value)
})
customArgs.CmdArgs = cmdArgs
if err := arguments.Execute(
generators.NameSystems(),