From 291b51ec50f9ea348748894386eec76ca494329b Mon Sep 17 00:00:00 2001 From: Tim Hockin Date: Mon, 6 Jun 2016 23:42:16 -0700 Subject: [PATCH] Use file tags to generate conversions This drives conversion generation from file tags like: // +conversion-gen=k8s.io/my/internal/version .. rather than hardcoded lists of packages. The only net change in generated code can be explained as correct. Previously it didn't know that conversion was available. --- .../conversion-gen/generators/conversion.go | 369 ++++++++++-------- .../go2idl/conversion-gen/generators/tags.go | 33 -- cmd/libs/go2idl/conversion-gen/main.go | 67 ++-- docs/devel/adding-an-APIGroup.md | 7 +- federation/apis/federation/v1beta1/doc.go | 2 +- hack/update-codegen.sh | 18 +- hack/verify-flags/known-flags.txt | 3 +- pkg/api/v1/doc.go | 2 +- pkg/api/v1/generated.proto | 4 +- pkg/api/v1/types.go | 4 +- pkg/apis/apps/v1alpha1/doc.go | 2 +- pkg/apis/authentication.k8s.io/v1beta1/doc.go | 2 +- pkg/apis/authorization/v1beta1/doc.go | 2 +- pkg/apis/autoscaling/v1/doc.go | 2 +- pkg/apis/batch/v1/doc.go | 2 +- pkg/apis/batch/v2alpha1/doc.go | 2 +- pkg/apis/certificates/v1alpha1/doc.go | 2 +- pkg/apis/componentconfig/v1alpha1/doc.go | 2 +- .../v1beta1/conversion_generated.go | 70 ++++ pkg/apis/extensions/v1beta1/doc.go | 4 +- pkg/apis/policy/v1alpha1/doc.go | 2 +- pkg/apis/rbac/v1alpha1/doc.go | 2 +- 22 files changed, 343 insertions(+), 260 deletions(-) delete mode 100644 cmd/libs/go2idl/conversion-gen/generators/tags.go diff --git a/cmd/libs/go2idl/conversion-gen/generators/conversion.go b/cmd/libs/go2idl/conversion-gen/generators/conversion.go index 5f347f00d3a..670a1bdb5ad 100644 --- a/cmd/libs/go2idl/conversion-gen/generators/conversion.go +++ b/cmd/libs/go2idl/conversion-gen/generators/conversion.go @@ -32,6 +32,19 @@ import ( "github.com/golang/glog" ) +// CustomArgs is used tby the go2idl framework to pass args specific to this +// generator. +type CustomArgs struct { + ExtraPeerDirs []string // Always consider these as last-ditch possibilities for conversions. +} + +// This is the comment tag that carries parameters for conversion generation. +const tagName = "k8s:conversion-gen" + +func extractTag(comments []string) []string { + return types.ExtractCommentTags("+", comments)[tagName] +} + // TODO: This is created only to reduce number of changes in a single PR. // Remove it and use PublicNamer instead. func conversionNamer() *namer.NameStrategy { @@ -67,25 +80,17 @@ func DefaultNameSystem() string { return "public" } -var fallbackPackages = []string{ - "k8s.io/kubernetes/pkg/api/unversioned", - "k8s.io/kubernetes/pkg/apis/extensions", - "k8s.io/kubernetes/pkg/apis/autoscaling", - "k8s.io/kubernetes/pkg/apis/batch", -} - -func getInternalTypeFor(context *generator.Context, t *types.Type) (*types.Type, bool) { - internalPackage := filepath.Dir(t.Name.Package) - if !context.Universe.Package(internalPackage).Has(t.Name.Name) { - for _, fallbackPackage := range fallbackPackages { - if fallbackPackage == t.Name.Package || !context.Universe.Package(fallbackPackage).Has(t.Name.Name) { - continue - } - return context.Universe.Package(fallbackPackage).Type(t.Name.Name), true +func getPeerTypeFor(context *generator.Context, t *types.Type, potenialPeerPkgs []string) *types.Type { + for _, ppp := range potenialPeerPkgs { + p := context.Universe.Package(ppp) + if p == nil { + continue + } + if p.Has(t.Name.Name) { + return p.Type(t.Name.Name) } - return nil, false } - return context.Universe.Package(internalPackage).Type(t.Name.Name), true + return nil } type conversionPair struct { @@ -97,109 +102,103 @@ type conversionPair struct { // the underlying type being "Func". type conversionFuncMap map[conversionPair]*types.Type -// Returns all manually-defined conversion functions that we are able to find. -func getManualConversionFunctions(context *generator.Context) conversionFuncMap { +// Returns all manually-defined conversion functions in the package. +func getManualConversionFunctions(context *generator.Context, pkg *types.Package, manualMap conversionFuncMap) { scopeName := types.Name{Package: conversionPackagePath, Name: "Scope"} errorName := types.Name{Package: "", Name: "error"} buffer := &bytes.Buffer{} sw := generator.NewSnippetWriter(buffer, context, "$", "$") - manualMap := make(conversionFuncMap) - for _, p := range context.Universe { - for _, f := range p.Functions { - if f.Underlying == nil || f.Underlying.Kind != types.Func { - glog.Errorf("Malformed function: %#v", f) - continue - } - if f.Underlying.Signature == nil { - glog.Errorf("Function without signature: %#v", f) - continue - } - signature := f.Underlying.Signature - // Check whether the function is conversion function. - // Note that all of them have signature: - // func Convert_inType_To_outType(inType, outType, conversion.Scope) error - if signature.Receiver != nil { - continue - } - if len(signature.Parameters) != 3 || signature.Parameters[2].Name != scopeName { - continue - } - if len(signature.Results) != 1 || signature.Results[0].Name != errorName { - continue - } - inType := signature.Parameters[0] - outType := signature.Parameters[1] - if inType.Kind != types.Pointer || outType.Kind != types.Pointer { - continue - } - // Now check if the name satisfies the convention. - args := argsFromType(inType.Elem, outType.Elem) - sw.Do("Convert_$.inType|public$_To_$.outType|public$", args) - if f.Name.Name == buffer.String() { - key := conversionPair{inType.Elem, outType.Elem} - if v, ok := manualMap[key]; ok && v != nil { - panic(fmt.Sprintf("duplicate static conversion defined: %#v", key)) - } - manualMap[key] = f - } - buffer.Reset() + for _, f := range pkg.Functions { + if f.Underlying == nil || f.Underlying.Kind != types.Func { + glog.Errorf("Malformed function: %#v", f) + continue } + if f.Underlying.Signature == nil { + glog.Errorf("Function without signature: %#v", f) + continue + } + signature := f.Underlying.Signature + // Check whether the function is conversion function. + // Note that all of them have signature: + // func Convert_inType_To_outType(inType, outType, conversion.Scope) error + if signature.Receiver != nil { + continue + } + if len(signature.Parameters) != 3 || signature.Parameters[2].Name != scopeName { + continue + } + if len(signature.Results) != 1 || signature.Results[0].Name != errorName { + continue + } + inType := signature.Parameters[0] + outType := signature.Parameters[1] + if inType.Kind != types.Pointer || outType.Kind != types.Pointer { + continue + } + // Now check if the name satisfies the convention. + args := argsFromType(inType.Elem, outType.Elem) + sw.Do("Convert_$.inType|public$_To_$.outType|public$", args) + if f.Name.Name == buffer.String() { + key := conversionPair{inType.Elem, outType.Elem} + // We might scan the same package twice, and that's OK. + if v, ok := manualMap[key]; ok && v != nil && v.Name.Package != pkg.Path { + panic(fmt.Sprintf("duplicate static conversion defined: %#v", key)) + } + manualMap[key] = f + } + buffer.Reset() } - return manualMap } // All of the types in conversions map are of type "DeclarationOf" with // the underlying type being "Func". type defaulterFuncMap map[*types.Type]*types.Type -// Returns all manually-defined defaulting functions that we are able to find. -func getManualDefaultingFunctions(context *generator.Context) defaulterFuncMap { +// Returns all manually-defined defaulting functions in the package. +func getManualDefaultingFunctions(context *generator.Context, pkg *types.Package, manualMap defaulterFuncMap) { buffer := &bytes.Buffer{} sw := generator.NewSnippetWriter(buffer, context, "$", "$") - manualMap := make(defaulterFuncMap) - for _, p := range context.Universe { - for _, f := range p.Functions { - if f.Underlying == nil || f.Underlying.Kind != types.Func { - glog.Errorf("Malformed function: %#v", f) - continue - } - if f.Underlying.Signature == nil { - glog.Errorf("Function without signature: %#v", f) - continue - } - signature := f.Underlying.Signature - // Check whether the function is conversion function. - // Note that all of them have signature: - // func Convert_inType_To_outType(inType, outType, conversion.Scope) error - if signature.Receiver != nil { - continue - } - if len(signature.Parameters) != 1 { - continue - } - if len(signature.Results) != 0 { - continue - } - inType := signature.Parameters[0] - if inType.Kind != types.Pointer { - continue - } - // Now check if the name satisfies the convention. - args := defaultingArgsFromType(inType.Elem) - sw.Do("$.inType|defaultfn$", args) - if f.Name.Name == buffer.String() { - key := inType.Elem - if v, ok := manualMap[key]; ok && v != nil { - panic(fmt.Sprintf("duplicate static defaulter defined: %#v", key)) - } - manualMap[key] = f - } - buffer.Reset() + for _, f := range pkg.Functions { + if f.Underlying == nil || f.Underlying.Kind != types.Func { + glog.Errorf("Malformed function: %#v", f) + continue } + if f.Underlying.Signature == nil { + glog.Errorf("Function without signature: %#v", f) + continue + } + signature := f.Underlying.Signature + // Check whether the function is conversion function. + // Note that all of them have signature: + // func Convert_inType_To_outType(inType, outType, conversion.Scope) error + if signature.Receiver != nil { + continue + } + if len(signature.Parameters) != 1 { + continue + } + if len(signature.Results) != 0 { + continue + } + inType := signature.Parameters[0] + if inType.Kind != types.Pointer { + continue + } + // Now check if the name satisfies the convention. + args := defaultingArgsFromType(inType.Elem) + sw.Do("$.inType|defaultfn$", args) + if f.Name.Name == buffer.String() { + key := inType.Elem + // We might scan the same package twice, and that's OK. + if v, ok := manualMap[key]; ok && v != nil && v.Name.Package != pkg.Path { + panic(fmt.Sprintf("duplicate static defaulter defined: %#v", key)) + } + manualMap[key] = f + } + buffer.Reset() } - return manualMap } func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages { @@ -208,7 +207,7 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat glog.Fatalf("Failed loading boilerplate: %v", err) } - inputs := sets.NewString(arguments.InputDirs...) + inputs := sets.NewString(context.Inputs...) packages := generator.Packages{} header := append([]byte(fmt.Sprintf("// +build !%s\n\n", arguments.GeneratedBuildTag)), boilerplate...) header = append(header, []byte( @@ -217,65 +216,89 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat `)...) - // Compute all pre-existing conversion functions. - manualConversions := getManualConversionFunctions(context) - manualDefaulters := getManualDefaultingFunctions(context) + // Accumulate pre-existing conversion and default functions. + // TODO: This is too ad-hoc. We need a better way. + manualConversions := conversionFuncMap{} + manualDefaults := defaulterFuncMap{} // We are generating conversions only for packages that are explicitly - // passed as InputDir, and only for those that have a corresponding type - // (in the directory one above) and can be automatically converted to. - for _, p := range context.Universe { - path := p.Path - if !inputs.Has(path) { - continue - } - // Only generate conversions for package which explicitly requested it - // byt setting "+genversion=true" in their doc.go file. - filtered := false - if extractBoolTagOrDie("genconversion", false, p.DocComments) == true { - filtered = true - } - if !filtered { + // passed as InputDir. + for i := range inputs { + glog.V(5).Infof("considering pkg %q", i) + pkg := context.Universe[i] + if pkg == nil { + // If the input had no Go files, for example. continue } - convertibleType := false - for _, t := range p.Types { - // Check whether this type can be auto-converted to the internal - // version. - internalType, exists := getInternalTypeFor(context, t) - if !exists { - // There is no corresponding type in the internal package. + // Add conversion and defaulting functions. + getManualConversionFunctions(context, pkg, manualConversions) + getManualDefaultingFunctions(context, pkg, manualDefaults) + + // Only generate conversions for packages which explicitly request it + // by specifying one or more "+k8s:conversion-gen=" + // in their doc.go file. + peerPkgs := extractTag(pkg.Comments) + if peerPkgs != nil { + glog.V(5).Infof(" tags: %q", peerPkgs) + } else { + glog.V(5).Infof(" no tag") + continue + } + if customArgs, ok := arguments.CustomArgs.(*CustomArgs); ok { + if len(customArgs.ExtraPeerDirs) > 0 { + peerPkgs = append(peerPkgs, customArgs.ExtraPeerDirs...) + } + } + // Make sure our peer-packages are added and fully parsed. + for _, pp := range peerPkgs { + context.AddDir(pp) + getManualConversionFunctions(context, context.Universe[pp], manualConversions) + getManualDefaultingFunctions(context, context.Universe[pp], manualDefaults) + } + + pkgNeedsGeneration := false + for _, t := range pkg.Types { + // Check whether this type can be auto-converted to the peer + // package type. + peerType := getPeerTypeFor(context, t, peerPkgs) + if peerType == nil { + // We did not find a corresponding type. continue } - // We won't be able to convert to private type. - if namer.IsPrivateGoName(internalType.Name.Name) { + if namer.IsPrivateGoName(peerType.Name.Name) { + // We won't be able to convert to a private type. + glog.V(5).Infof(" found a peer type %v, but it is a private name", t) continue } + // If we can generate conversion in any direction, we should // generate this package. - if isConvertible(t, internalType, manualConversions) || isConvertible(internalType, t, manualConversions) { - convertibleType = true + if isConvertible(t, peerType, manualConversions) || isConvertible(peerType, t, manualConversions) { + pkgNeedsGeneration = true + break } } - - if convertibleType { - packages = append(packages, - &generator.DefaultPackage{ - PackageName: filepath.Base(path), - PackagePath: path, - HeaderText: header, - GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { - generators = []generator.Generator{} - generators = append( - generators, NewGenConversion("conversion_generated", path, manualConversions, manualDefaulters)) - return generators - }, - FilterFunc: func(c *generator.Context, t *types.Type) bool { - return t.Name.Package == path - }, - }) + if !pkgNeedsGeneration { + glog.V(5).Infof(" no viable conversions, not generating for this package") + continue } + + packages = append(packages, + &generator.DefaultPackage{ + PackageName: filepath.Base(pkg.Path), + PackagePath: pkg.Path, + HeaderText: header, + GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { + generators = []generator.Generator{} + generators = append( + generators, NewGenConversion(arguments.OutputFileBaseName, pkg.Path, manualConversions, manualDefaults, peerPkgs)) + return generators + }, + FilterFunc: func(c *generator.Context, t *types.Type) bool { + return t.Name.Package == pkg.Path + }, + }) } return packages } @@ -324,9 +347,6 @@ func isDirectlyConvertible(in, out *types.Type, manualConversions conversionFunc switch in.Kind { case types.Builtin: - if in == out { - return true - } // TODO: Support more conversion types. return types.IsInteger(in) && types.IsInteger(out) case types.Struct: @@ -335,11 +355,11 @@ func isDirectlyConvertible(in, out *types.Type, manualConversions conversionFunc // Check if there is an out member with that name. outMember, found := findMember(out, inMember.Name) if !found { - // Check if the member doesn't have comment: - // "+ genconversion=false" - // comment to ignore this field for conversion. + // Check if the member has opted out with: + // "+k8s:conversion-gen=false" // TODO: Switch to SecondClosestCommentLines. - if extractBoolTagOrDie("genconversion", true, inMember.CommentLines) == false { + if tagvals := extractTag(inMember.CommentLines); tagvals != nil && tagvals[0] == "false" { + glog.V(5).Infof("field %v.%s requests no conversion generation, skipping", in, inMember.Name) continue } return false @@ -387,6 +407,7 @@ const ( type genConversion struct { generator.DefaultGen targetPackage string + peerPackages []string manualConversions conversionFuncMap manualDefaulters defaulterFuncMap imports namer.ImportTracker @@ -395,12 +416,13 @@ type genConversion struct { globalVariables map[string]interface{} } -func NewGenConversion(sanitizedName, targetPackage string, manualConversions conversionFuncMap, manualDefaulters defaulterFuncMap) generator.Generator { +func NewGenConversion(sanitizedName, targetPackage string, manualConversions conversionFuncMap, manualDefaulters defaulterFuncMap, peerPkgs []string) generator.Generator { return &genConversion{ DefaultGen: generator.DefaultGen{ OptionalName: sanitizedName, }, targetPackage: targetPackage, + peerPackages: peerPkgs, manualConversions: manualConversions, manualDefaulters: manualDefaulters, imports: generator.NewImportTracker(), @@ -425,7 +447,13 @@ func (g *genConversion) convertibleOnlyWithinPackage(inType, outType *types.Type if t.Name.Package != g.targetPackage { return false } - if extractBoolTagOrDie("genconversion", true, t.CommentLines) == false { + // If the type has opted out, skip it. + tagvals := extractTag(t.CommentLines) + if tagvals != nil { + if tagvals[0] != "false" { + glog.Fatalf("Type %v: unsupported %s value: %q", t, tagName, tagvals[0]) + } + glog.V(5).Infof("type %v requests no conversion generation, skipping", t) return false } // TODO: Consider generating functions for other kinds too. @@ -440,22 +468,22 @@ func (g *genConversion) convertibleOnlyWithinPackage(inType, outType *types.Type } func (g *genConversion) Filter(c *generator.Context, t *types.Type) bool { - internalType, exists := getInternalTypeFor(c, t) - if !exists { + peerType := getPeerTypeFor(c, t, g.peerPackages) + if peerType == nil { return false } - if !g.convertibleOnlyWithinPackage(t, internalType) { + if !g.convertibleOnlyWithinPackage(t, peerType) { return false } // We explicitly return true if any conversion is possible - this needs // to be checked again while generating code for that type. convertible := false - if isConvertible(t, internalType, g.manualConversions) { - g.typesForInit = append(g.typesForInit, conversionPair{t, internalType}) + if isConvertible(t, peerType, g.manualConversions) { + g.typesForInit = append(g.typesForInit, conversionPair{t, peerType}) convertible = true } - if isConvertible(internalType, t, g.manualConversions) { - g.typesForInit = append(g.typesForInit, conversionPair{internalType, t}) + if isConvertible(peerType, t, g.manualConversions) { + g.typesForInit = append(g.typesForInit, conversionPair{peerType, t}) convertible = true } return convertible @@ -542,13 +570,14 @@ func (g *genConversion) Init(c *generator.Context, w io.Writer) error { } func (g *genConversion) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + glog.V(5).Infof("generating for type %v", t) sw := generator.NewSnippetWriter(w, c, "$", "$") - internalType, _ := getInternalTypeFor(c, t) - if isDirectlyConvertible(t, internalType, g.manualConversions) { - g.generateConversion(t, internalType, sw) + peerType := getPeerTypeFor(c, t, g.peerPackages) + if isDirectlyConvertible(t, peerType, g.manualConversions) { + g.generateConversion(t, peerType, sw) } - if isDirectlyConvertible(internalType, t, g.manualConversions) { - g.generateConversion(internalType, t, sw) + if isDirectlyConvertible(peerType, t, g.manualConversions) { + g.generateConversion(peerType, t, sw) } return sw.Error() } @@ -681,7 +710,7 @@ func (g *genConversion) doStruct(inType, outType *types.Type, sw *generator.Snip outMember, isOutMember := findMember(outType, m.Name) if !isOutMember { // Since this object wasn't filtered out, this means that - // this field has "genconversion=false" comment to ignore it. + // this field has "+k8s:conversion-gen=false" comment to ignore it. continue } t, outT := m.Type, outMember.Type diff --git a/cmd/libs/go2idl/conversion-gen/generators/tags.go b/cmd/libs/go2idl/conversion-gen/generators/tags.go deleted file mode 100644 index 032c501e3de..00000000000 --- a/cmd/libs/go2idl/conversion-gen/generators/tags.go +++ /dev/null @@ -1,33 +0,0 @@ -/* -Copyright 2016 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 ( - "github.com/golang/glog" - "k8s.io/kubernetes/cmd/libs/go2idl/types" -) - -// extractBoolTagOrDie gets the comment-tags for the key and asserts that, if -// it exists, the value is boolean. If the tag did not exist, it returns -// defaultVal. -func extractBoolTagOrDie(key string, defaultVal bool, lines []string) bool { - val, err := types.ExtractSingleBoolCommentTag("+", key, defaultVal, lines) - if err != nil { - glog.Fatalf(err.Error()) - } - return val -} diff --git a/cmd/libs/go2idl/conversion-gen/main.go b/cmd/libs/go2idl/conversion-gen/main.go index 5b2dae836fc..33a4001370e 100644 --- a/cmd/libs/go2idl/conversion-gen/main.go +++ b/cmd/libs/go2idl/conversion-gen/main.go @@ -16,9 +16,22 @@ limitations under the License. // conversion-gen is a tool for auto-generating Conversion functions. // -// Structs in the input directories with the below line in their comments -// will be ignored during generation. -// // +genconversion=false +// Given a list of input directories, it will scan for "peer" packages and +// generate functions that efficiently convert between same-name types in each +// package. For any pair of types that has a +// `Convert___To__ +// +// When generating for a package, individual types or fields of structs may opt +// out of Conversion generation by specifying a comment on the of the form: +// // +k8s:conversion-gen=false package main import ( @@ -26,42 +39,30 @@ import ( "k8s.io/kubernetes/cmd/libs/go2idl/conversion-gen/generators" "github.com/golang/glog" + "github.com/spf13/pflag" ) func main() { arguments := args.Default() - // Override defaults. These are Kubernetes specific input locations. - arguments.InputDirs = []string{ - "k8s.io/kubernetes/pkg/api/v1", - "k8s.io/kubernetes/pkg/api", - "k8s.io/kubernetes/pkg/apis/authentication.k8s.io", - "k8s.io/kubernetes/pkg/apis/authentication.k8s.io/v1beta1", - "k8s.io/kubernetes/pkg/apis/authorization", - "k8s.io/kubernetes/pkg/apis/authorization/v1beta1", - "k8s.io/kubernetes/pkg/apis/autoscaling", - "k8s.io/kubernetes/pkg/apis/autoscaling/v1", - "k8s.io/kubernetes/pkg/apis/batch", - "k8s.io/kubernetes/pkg/apis/batch/v1", - "k8s.io/kubernetes/pkg/apis/batch/v2alpha1", - "k8s.io/kubernetes/pkg/apis/apps", - "k8s.io/kubernetes/pkg/apis/apps/v1alpha1", - "k8s.io/kubernetes/pkg/apis/certificates", - "k8s.io/kubernetes/pkg/apis/certificates/v1alpha1", - "k8s.io/kubernetes/pkg/apis/componentconfig", - "k8s.io/kubernetes/pkg/apis/componentconfig/v1alpha1", - "k8s.io/kubernetes/pkg/apis/policy", - "k8s.io/kubernetes/pkg/apis/policy/v1alpha1", - "k8s.io/kubernetes/pkg/apis/extensions", - "k8s.io/kubernetes/pkg/apis/extensions/v1beta1", - "k8s.io/kubernetes/pkg/apis/rbac", - "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1", - "k8s.io/kubernetes/federation/apis/federation", - "k8s.io/kubernetes/federation/apis/federation/v1beta1", - "k8s.io/kubernetes/pkg/conversion", - "k8s.io/kubernetes/pkg/runtime", - } + // Override defaults. + arguments.OutputFileBaseName = "conversion_generated" + // Custom args. + customArgs := &generators.CustomArgs{ + ExtraPeerDirs: []string{ + "k8s.io/kubernetes/pkg/api", + "k8s.io/kubernetes/pkg/api/v1", + "k8s.io/kubernetes/pkg/api/unversioned", + "k8s.io/kubernetes/pkg/conversion", + "k8s.io/kubernetes/pkg/runtime", + }, + } + pflag.CommandLine.StringSliceVar(&customArgs.ExtraPeerDirs, "extra-peer-dirs", customArgs.ExtraPeerDirs, + "Comma-separated list of import paths which are considered, after tag-specified peers, for conversions.") + arguments.CustomArgs = customArgs + + // Run it. if err := arguments.Execute( generators.NameSystems(), generators.DefaultNameSystem(), diff --git a/docs/devel/adding-an-APIGroup.md b/docs/devel/adding-an-APIGroup.md index 63c4e2a2d69..cefa8564144 100644 --- a/docs/devel/adding-an-APIGroup.md +++ b/docs/devel/adding-an-APIGroup.md @@ -79,9 +79,10 @@ cmd/libs/go2idl/ tool. 2. Make sure your pkg/apis/``/`` directory has a doc.go file with the comment `// +k8s:deepcopy-gen=package,register`, to catch the attention of our generation tools. - 3. Make sure your pkg/apis/``/`` directory has a doc.go file - with the comment `// +genconversion=true`, to catch the attention of our - gen-conversion script. + 3. Make sure your `pkg/apis//` directory has a doc.go file + with the comment `// +k8s:conversion-gen=`, to catch the + attention of our generation tools. For most APIs the only target you + need is `k8s.io/kubernetes/pkg/apis/` (your internal API). 4. Run hack/update-all.sh. 2. Generate files for Ugorji codec: diff --git a/federation/apis/federation/v1beta1/doc.go b/federation/apis/federation/v1beta1/doc.go index 8eebd4e8795..3a4f89c982e 100644 --- a/federation/apis/federation/v1beta1/doc.go +++ b/federation/apis/federation/v1beta1/doc.go @@ -15,6 +15,6 @@ limitations under the License. */ // +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=k8s.io/kubernetes/federation/apis/federation -// +genconversion=true package v1beta1 diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index 9d5df48b903..2eba4de2f27 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -47,7 +47,6 @@ ${clientgen} --clientset-name="release_1_4" --input="api/v1,extensions/v1beta1,a # Clientgen for federation clientset. ${clientgen} --clientset-name=federation_internalclientset --clientset-path=k8s.io/kubernetes/federation/client/clientset_generated --input="../../federation/apis/federation/","api/" --included-types-overrides="api/Service" "$@" ${clientgen} --clientset-name=federation_release_1_4 --clientset-path=k8s.io/kubernetes/federation/client/clientset_generated --input="../../federation/apis/federation/v1beta1","api/v1" --included-types-overrides="api/v1/Service" "$@" -${conversiongen} "$@" ${setgen} "$@" # You may add additional calls of code generators like set-gen above. @@ -73,9 +72,22 @@ DEEP_COPY_DIRS=$( | xargs dirname \ | sort -u ) -INPUTS=$( +DEEPCOPY_INPUTS=$( for d in ${DEEP_COPY_DIRS}; do echo k8s.io/kubernetes/$d done | paste -sd, ) -${deepcopygen} -i ${INPUTS} +${deepcopygen} -i ${DEEPCOPY_INPUTS} + +CONVERSION_DIRS=$( + grep '^// *+k8s:conversion-gen=' ${ALL_K8S_TAG_FILES} \ + | cut -f1 -d: \ + | xargs dirname \ + | sort -u \ + ) +CONVERSION_INPUTS=$( + for d in ${CONVERSION_DIRS}; do + echo k8s.io/kubernetes/$d + done | paste -sd, + ) +${conversiongen} -i ${CONVERSION_INPUTS} diff --git a/hack/verify-flags/known-flags.txt b/hack/verify-flags/known-flags.txt index e85f40967cc..b9dd7fcb57d 100644 --- a/hack/verify-flags/known-flags.txt +++ b/hack/verify-flags/known-flags.txt @@ -150,13 +150,14 @@ executor-bindall executor-logv executor-path executor-suicide-timeout +exit-on-lock-contention experimental-flannel-overlay experimental-keystone-url experimental-nvidia-gpus experimental-prefix external-hostname external-ip -exit-on-lock-contention +extra-peer-dirs failover-timeout failure-domains fake-clientset diff --git a/pkg/api/v1/doc.go b/pkg/api/v1/doc.go index 0d81e264a06..8849ee1cb13 100644 --- a/pkg/api/v1/doc.go +++ b/pkg/api/v1/doc.go @@ -15,7 +15,7 @@ limitations under the License. */ // +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/api // Package v1 is the v1 version of the API. -// +genconversion=true package v1 diff --git a/pkg/api/v1/generated.proto b/pkg/api/v1/generated.proto index 86fbcabb3ef..1aef723d98d 100644 --- a/pkg/api/v1/generated.proto +++ b/pkg/api/v1/generated.proto @@ -2481,7 +2481,7 @@ message Secret { // It is provided as a write-only convenience method. // All keys and values are merged into the data field on write, overwriting any existing values. // It is never output when reading from the API. - // +genconversion=false + // +k8s:conversion-gen=false map stringData = 4; // Used to facilitate programmatic handling of secret data. @@ -2715,7 +2715,7 @@ message ServiceSpec { // API for compatibility until at least 8/20/2016. It will be removed from // any new API revisions. If both deprecatedPublicIPs *and* externalIPs are // set, deprecatedPublicIPs is used. - // +genconversion=false + // +k8s:conversion-gen=false repeated string deprecatedPublicIPs = 6; // Supports "ClientIP" and "None". Used to maintain session affinity. diff --git a/pkg/api/v1/types.go b/pkg/api/v1/types.go index 9cb0ac558a2..d09101b8bde 100644 --- a/pkg/api/v1/types.go +++ b/pkg/api/v1/types.go @@ -2081,7 +2081,7 @@ type ServiceSpec struct { // API for compatibility until at least 8/20/2016. It will be removed from // any new API revisions. If both deprecatedPublicIPs *and* externalIPs are // set, deprecatedPublicIPs is used. - // +genconversion=false + // +k8s:conversion-gen=false DeprecatedPublicIPs []string `json:"deprecatedPublicIPs,omitempty" protobuf:"bytes,6,rep,name=deprecatedPublicIPs"` // Supports "ClientIP" and "None". Used to maintain session affinity. @@ -3096,7 +3096,7 @@ type Secret struct { // It is provided as a write-only convenience method. // All keys and values are merged into the data field on write, overwriting any existing values. // It is never output when reading from the API. - // +genconversion=false + // +k8s:conversion-gen=false StringData map[string]string `json:"stringData,omitempty" protobuf:"bytes,4,rep,name=stringData"` // Used to facilitate programmatic handling of secret data. diff --git a/pkg/apis/apps/v1alpha1/doc.go b/pkg/apis/apps/v1alpha1/doc.go index 97c790d9da3..53d9fcabc41 100644 --- a/pkg/apis/apps/v1alpha1/doc.go +++ b/pkg/apis/apps/v1alpha1/doc.go @@ -15,6 +15,6 @@ limitations under the License. */ // +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/apps -// +genconversion=true package v1alpha1 diff --git a/pkg/apis/authentication.k8s.io/v1beta1/doc.go b/pkg/apis/authentication.k8s.io/v1beta1/doc.go index 8eebd4e8795..25b76fd347f 100644 --- a/pkg/apis/authentication.k8s.io/v1beta1/doc.go +++ b/pkg/apis/authentication.k8s.io/v1beta1/doc.go @@ -15,6 +15,6 @@ limitations under the License. */ // +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/authentication.k8s.io -// +genconversion=true package v1beta1 diff --git a/pkg/apis/authorization/v1beta1/doc.go b/pkg/apis/authorization/v1beta1/doc.go index 8eebd4e8795..a6f2c27b10e 100644 --- a/pkg/apis/authorization/v1beta1/doc.go +++ b/pkg/apis/authorization/v1beta1/doc.go @@ -15,6 +15,6 @@ limitations under the License. */ // +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/authorization -// +genconversion=true package v1beta1 diff --git a/pkg/apis/autoscaling/v1/doc.go b/pkg/apis/autoscaling/v1/doc.go index 0dc08faf0a7..be1c70fdb5a 100644 --- a/pkg/apis/autoscaling/v1/doc.go +++ b/pkg/apis/autoscaling/v1/doc.go @@ -15,6 +15,6 @@ limitations under the License. */ // +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/autoscaling -// +genconversion=true package v1 diff --git a/pkg/apis/batch/v1/doc.go b/pkg/apis/batch/v1/doc.go index 0dc08faf0a7..5695b9e4022 100644 --- a/pkg/apis/batch/v1/doc.go +++ b/pkg/apis/batch/v1/doc.go @@ -15,6 +15,6 @@ limitations under the License. */ // +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/batch -// +genconversion=true package v1 diff --git a/pkg/apis/batch/v2alpha1/doc.go b/pkg/apis/batch/v2alpha1/doc.go index c7cf93b9f98..76b5d325131 100644 --- a/pkg/apis/batch/v2alpha1/doc.go +++ b/pkg/apis/batch/v2alpha1/doc.go @@ -15,6 +15,6 @@ limitations under the License. */ // +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/batch -// +genconversion=true package v2alpha1 diff --git a/pkg/apis/certificates/v1alpha1/doc.go b/pkg/apis/certificates/v1alpha1/doc.go index 97c790d9da3..3528c164f9a 100644 --- a/pkg/apis/certificates/v1alpha1/doc.go +++ b/pkg/apis/certificates/v1alpha1/doc.go @@ -15,6 +15,6 @@ limitations under the License. */ // +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/certificates -// +genconversion=true package v1alpha1 diff --git a/pkg/apis/componentconfig/v1alpha1/doc.go b/pkg/apis/componentconfig/v1alpha1/doc.go index 97c790d9da3..621e80613d1 100644 --- a/pkg/apis/componentconfig/v1alpha1/doc.go +++ b/pkg/apis/componentconfig/v1alpha1/doc.go @@ -15,6 +15,6 @@ limitations under the License. */ // +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/componentconfig -// +genconversion=true package v1alpha1 diff --git a/pkg/apis/extensions/v1beta1/conversion_generated.go b/pkg/apis/extensions/v1beta1/conversion_generated.go index 727b26e8867..6f4ff538bfa 100644 --- a/pkg/apis/extensions/v1beta1/conversion_generated.go +++ b/pkg/apis/extensions/v1beta1/conversion_generated.go @@ -62,6 +62,8 @@ func init() { Convert_extensions_DeploymentStatus_To_v1beta1_DeploymentStatus, Convert_v1beta1_DeploymentStrategy_To_extensions_DeploymentStrategy, Convert_extensions_DeploymentStrategy_To_v1beta1_DeploymentStrategy, + Convert_v1beta1_ExportOptions_To_api_ExportOptions, + Convert_api_ExportOptions_To_v1beta1_ExportOptions, Convert_v1beta1_FSGroupStrategyOptions_To_extensions_FSGroupStrategyOptions, Convert_extensions_FSGroupStrategyOptions_To_v1beta1_FSGroupStrategyOptions, Convert_v1beta1_HTTPIngressPath_To_extensions_HTTPIngressPath, @@ -110,6 +112,8 @@ func init() { Convert_unversioned_LabelSelector_To_v1beta1_LabelSelector, Convert_v1beta1_LabelSelectorRequirement_To_unversioned_LabelSelectorRequirement, Convert_unversioned_LabelSelectorRequirement_To_v1beta1_LabelSelectorRequirement, + Convert_v1beta1_ListOptions_To_api_ListOptions, + Convert_api_ListOptions_To_v1beta1_ListOptions, Convert_v1beta1_NetworkPolicy_To_extensions_NetworkPolicy, Convert_extensions_NetworkPolicy_To_v1beta1_NetworkPolicy, Convert_v1beta1_NetworkPolicyIngressRule_To_extensions_NetworkPolicyIngressRule, @@ -644,6 +648,32 @@ func autoConvert_extensions_DeploymentStrategy_To_v1beta1_DeploymentStrategy(in return nil } +func autoConvert_v1beta1_ExportOptions_To_api_ExportOptions(in *ExportOptions, out *api.ExportOptions, s conversion.Scope) error { + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + out.Export = in.Export + out.Exact = in.Exact + return nil +} + +func Convert_v1beta1_ExportOptions_To_api_ExportOptions(in *ExportOptions, out *api.ExportOptions, s conversion.Scope) error { + return autoConvert_v1beta1_ExportOptions_To_api_ExportOptions(in, out, s) +} + +func autoConvert_api_ExportOptions_To_v1beta1_ExportOptions(in *api.ExportOptions, out *ExportOptions, s conversion.Scope) error { + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + out.Export = in.Export + out.Exact = in.Exact + return nil +} + +func Convert_api_ExportOptions_To_v1beta1_ExportOptions(in *api.ExportOptions, out *ExportOptions, s conversion.Scope) error { + return autoConvert_api_ExportOptions_To_v1beta1_ExportOptions(in, out, s) +} + func autoConvert_v1beta1_FSGroupStrategyOptions_To_extensions_FSGroupStrategyOptions(in *FSGroupStrategyOptions, out *extensions.FSGroupStrategyOptions, s conversion.Scope) error { out.Rule = extensions.FSGroupStrategyType(in.Rule) if in.Ranges != nil { @@ -1440,6 +1470,46 @@ func Convert_unversioned_LabelSelectorRequirement_To_v1beta1_LabelSelectorRequir return autoConvert_unversioned_LabelSelectorRequirement_To_v1beta1_LabelSelectorRequirement(in, out, s) } +func autoConvert_v1beta1_ListOptions_To_api_ListOptions(in *ListOptions, out *api.ListOptions, s conversion.Scope) error { + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + if err := api.Convert_string_To_labels_Selector(&in.LabelSelector, &out.LabelSelector, s); err != nil { + return err + } + if err := api.Convert_string_To_fields_Selector(&in.FieldSelector, &out.FieldSelector, s); err != nil { + return err + } + out.Watch = in.Watch + out.ResourceVersion = in.ResourceVersion + out.TimeoutSeconds = in.TimeoutSeconds + return nil +} + +func Convert_v1beta1_ListOptions_To_api_ListOptions(in *ListOptions, out *api.ListOptions, s conversion.Scope) error { + return autoConvert_v1beta1_ListOptions_To_api_ListOptions(in, out, s) +} + +func autoConvert_api_ListOptions_To_v1beta1_ListOptions(in *api.ListOptions, out *ListOptions, s conversion.Scope) error { + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + if err := api.Convert_labels_Selector_To_string(&in.LabelSelector, &out.LabelSelector, s); err != nil { + return err + } + if err := api.Convert_fields_Selector_To_string(&in.FieldSelector, &out.FieldSelector, s); err != nil { + return err + } + out.Watch = in.Watch + out.ResourceVersion = in.ResourceVersion + out.TimeoutSeconds = in.TimeoutSeconds + return nil +} + +func Convert_api_ListOptions_To_v1beta1_ListOptions(in *api.ListOptions, out *ListOptions, s conversion.Scope) error { + return autoConvert_api_ListOptions_To_v1beta1_ListOptions(in, out, s) +} + func autoConvert_v1beta1_NetworkPolicy_To_extensions_NetworkPolicy(in *NetworkPolicy, out *extensions.NetworkPolicy, s conversion.Scope) error { SetDefaults_NetworkPolicy(in) if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { diff --git a/pkg/apis/extensions/v1beta1/doc.go b/pkg/apis/extensions/v1beta1/doc.go index 8eebd4e8795..dc8790024d9 100644 --- a/pkg/apis/extensions/v1beta1/doc.go +++ b/pkg/apis/extensions/v1beta1/doc.go @@ -15,6 +15,8 @@ limitations under the License. */ // +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/extensions +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/autoscaling +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/batch -// +genconversion=true package v1beta1 diff --git a/pkg/apis/policy/v1alpha1/doc.go b/pkg/apis/policy/v1alpha1/doc.go index 37d78e29170..985d4bbf071 100644 --- a/pkg/apis/policy/v1alpha1/doc.go +++ b/pkg/apis/policy/v1alpha1/doc.go @@ -15,9 +15,9 @@ limitations under the License. */ // +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/policy // Package policy is for any kind of policy object. Suitable examples, even if // they aren't all here, are PodDisruptionBudget, PodSecurityPolicy, // NetworkPolicy, etc. -// +genconversion=true package v1alpha1 diff --git a/pkg/apis/rbac/v1alpha1/doc.go b/pkg/apis/rbac/v1alpha1/doc.go index 35aef40894f..e471bd384bf 100644 --- a/pkg/apis/rbac/v1alpha1/doc.go +++ b/pkg/apis/rbac/v1alpha1/doc.go @@ -16,6 +16,6 @@ limitations under the License. // +groupName=rbac.authorization.k8s.io // +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/rbac -// +genconversion=true package v1alpha1