diff --git a/staging/src/BUILD b/staging/src/BUILD index d4744ab8f01c3..c49c60ba4527f 100644 --- a/staging/src/BUILD +++ b/staging/src/BUILD @@ -188,6 +188,7 @@ filegroup( "//staging/src/k8s.io/code-generator/cmd/informer-gen:all-srcs", "//staging/src/k8s.io/code-generator/cmd/lister-gen:all-srcs", "//staging/src/k8s.io/code-generator/cmd/openapi-gen:all-srcs", + "//staging/src/k8s.io/code-generator/cmd/register-gen:all-srcs", "//staging/src/k8s.io/code-generator/cmd/set-gen:all-srcs", "//staging/src/k8s.io/code-generator/hack:all-srcs", "//staging/src/k8s.io/code-generator/pkg/util:all-srcs", diff --git a/staging/src/k8s.io/code-generator/cmd/register-gen/BUILD b/staging/src/k8s.io/code-generator/cmd/register-gen/BUILD new file mode 100644 index 0000000000000..bdf68063d9f73 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/register-gen/BUILD @@ -0,0 +1,41 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_library( + name = "go_default_library", + srcs = ["main.go"], + importmap = "k8s.io/kubernetes/vendor/k8s.io/code-generator/cmd/register-gen", + importpath = "k8s.io/code-generator/cmd/register-gen", + visibility = ["//visibility:private"], + deps = [ + "//staging/src/k8s.io/code-generator/cmd/register-gen/args:go_default_library", + "//staging/src/k8s.io/code-generator/cmd/register-gen/generators:go_default_library", + "//staging/src/k8s.io/code-generator/pkg/util:go_default_library", + "//vendor/github.com/golang/glog:go_default_library", + "//vendor/github.com/spf13/pflag:go_default_library", + "//vendor/k8s.io/gengo/args:go_default_library", + ], +) + +go_binary( + name = "register-gen", + embed = [":go_default_library"], + visibility = ["//visibility:public"], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [ + ":package-srcs", + "//staging/src/k8s.io/code-generator/cmd/register-gen/args:all-srcs", + "//staging/src/k8s.io/code-generator/cmd/register-gen/generators:all-srcs", + ], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/staging/src/k8s.io/code-generator/cmd/register-gen/args/BUILD b/staging/src/k8s.io/code-generator/cmd/register-gen/args/BUILD new file mode 100644 index 0000000000000..62235b6d3fcfb --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/register-gen/args/BUILD @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["args.go"], + importmap = "k8s.io/kubernetes/vendor/k8s.io/code-generator/cmd/register-gen/args", + importpath = "k8s.io/code-generator/cmd/register-gen/args", + visibility = ["//visibility:public"], + deps = ["//vendor/k8s.io/gengo/args:go_default_library"], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/staging/src/k8s.io/code-generator/cmd/register-gen/args/args.go b/staging/src/k8s.io/code-generator/cmd/register-gen/args/args.go new file mode 100644 index 0000000000000..2e3ab084e2019 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/register-gen/args/args.go @@ -0,0 +1,39 @@ +/* +Copyright 2018 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" + + "k8s.io/gengo/args" +) + +// NewDefaults returns default arguments for the generator. +func NewDefaults() *args.GeneratorArgs { + genericArgs := args.Default().WithoutDefaultFlagParsing() + genericArgs.OutputFileBaseName = "zz_generated.register" + return genericArgs +} + +// Validate checks the given arguments. +func Validate(genericArgs *args.GeneratorArgs) error { + if len(genericArgs.OutputFileBaseName) == 0 { + return fmt.Errorf("output file base name cannot be empty") + } + + return nil +} diff --git a/staging/src/k8s.io/code-generator/cmd/register-gen/generators/BUILD b/staging/src/k8s.io/code-generator/cmd/register-gen/generators/BUILD new file mode 100644 index 0000000000000..810ae444f1478 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/register-gen/generators/BUILD @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "packages.go", + "register_external.go", + ], + importmap = "k8s.io/kubernetes/vendor/k8s.io/code-generator/cmd/register-gen/generators", + importpath = "k8s.io/code-generator/cmd/register-gen/generators", + visibility = ["//visibility:public"], + deps = [ + "//staging/src/k8s.io/code-generator/cmd/client-gen/types:go_default_library", + "//vendor/github.com/golang/glog:go_default_library", + "//vendor/k8s.io/gengo/args:go_default_library", + "//vendor/k8s.io/gengo/generator:go_default_library", + "//vendor/k8s.io/gengo/namer:go_default_library", + "//vendor/k8s.io/gengo/types:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/staging/src/k8s.io/code-generator/cmd/register-gen/generators/packages.go b/staging/src/k8s.io/code-generator/cmd/register-gen/generators/packages.go new file mode 100644 index 0000000000000..ffcea0f0f7431 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/register-gen/generators/packages.go @@ -0,0 +1,140 @@ +/* +Copyright 2018 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 ( + "os" + "path" + "strings" + + "github.com/golang/glog" + + clientgentypes "k8s.io/code-generator/cmd/client-gen/types" + "k8s.io/gengo/args" + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" + "fmt" +) + +// NameSystems returns the name system used by the generators in this package. +func NameSystems() namer.NameSystems { + return namer.NameSystems{} +} + +// DefaultNameSystem returns the default name system for ordering the types to be +// processed by the generators in this package. +func DefaultNameSystem() string { + return "public" +} + +// Packages makes packages to generate. +func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages { + boilerplate, err := arguments.LoadGoBoilerplate() + if err != nil { + glog.Fatalf("Failed loading boilerplate: %v", err) + } + + packages := generator.Packages{} + for _, inputDir := range arguments.InputDirs { + + pkg := context.Universe.Package(inputDir) + internal, err := isInternalType(pkg) + if err != nil { + glog.V(5).Infof("skipping the generation of %s file, due to err %v",arguments.OutputFileBaseName, err) + continue + } + if internal { + glog.V(5).Infof("skipping the generation of %s file because %s package contains internal types, note that internal types don't have \"json\" tags", arguments.OutputFileBaseName, pkg.Name) + continue + } + registerFileName := "register.go.BAK" + searchPath := path.Join(args.DefaultSourceTree(), inputDir, registerFileName) + if _, err := os.Stat(path.Join(searchPath)); err == nil { + glog.V(5).Infof("skipping the generation of %s file because %s already exists in the path %s", arguments.OutputFileBaseName, registerFileName, searchPath) + continue + } else if err != nil && !os.IsNotExist(err) { + glog.Fatalf("an error %v has occurred while checking if %s exists", err, registerFileName) + } + + gv := clientgentypes.GroupVersion{} + { + pathParts := strings.Split(pkg.Path, "/") + if len(pathParts) < 2 { + glog.Errorf("the path of the package must contain the group name and the version, path = %s", pkg.Path) + continue + } + gv.Group = clientgentypes.Group(pathParts[len(pathParts)-2]) + gv.Version = clientgentypes.Version(pathParts[len(pathParts)-1]) + + // if there is a comment of the form "// +groupName=somegroup" or "// +groupName=somegroup.foo.bar.io", + // extract the fully qualified API group name from it and overwrite the group inferred from the package path + if override := types.ExtractCommentTags("+", pkg.DocComments)["groupName"]; override != nil { + groupName := override[0] + glog.V(5).Infof("overriding the group name with = %s", groupName) + gv.Group = clientgentypes.Group(groupName) + } + } + + typesToRegister := []*types.Type{} + for _, t := range pkg.Types { + glog.V(5).Infof("considering type = %s", t.Name.String()) + for _, typeMember := range t.Members { + if typeMember.Name == "TypeMeta" && typeMember.Embedded == true { + typesToRegister = append(typesToRegister, t) + } + } + } + + packages = append(packages, + &generator.DefaultPackage{ + PackageName: pkg.Name, + PackagePath: pkg.Path, + HeaderText: boilerplate, + GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { + return []generator.Generator{ + ®isterExternalGenerator{ + DefaultGen: generator.DefaultGen{ + OptionalName: arguments.OutputFileBaseName, + }, + gv: gv, + typesToGenerate: typesToRegister, + outputPackage: pkg.Path, + imports: generator.NewImportTracker(), + }, + } + }, + }) + } + + return packages +} + +// isInternalType determines whether the given package +// contains the internal types +func isInternalType(p *types.Package) (bool, error) { + for _, t := range p.Types { + for _, member := range t.Members { + if member.Name == "TypeMeta" { + return !strings.Contains(member.Tags, "json"), nil + } + } + } + return false, fmt.Errorf("unable to find TypeMeta for any types in package %s", p.Path) +} + + diff --git a/staging/src/k8s.io/code-generator/cmd/register-gen/generators/register_external.go b/staging/src/k8s.io/code-generator/cmd/register-gen/generators/register_external.go new file mode 100644 index 0000000000000..5ef151b649fb9 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/register-gen/generators/register_external.go @@ -0,0 +1,117 @@ +/* +Copyright 2018 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 ( + "io" + "sort" + + clientgentypes "k8s.io/code-generator/cmd/client-gen/types" + "k8s.io/gengo/generator" + "k8s.io/gengo/namer" + "k8s.io/gengo/types" +) + +type registerExternalGenerator struct { + generator.DefaultGen + outputPackage string + gv clientgentypes.GroupVersion + typesToGenerate []*types.Type + imports namer.ImportTracker +} + +var _ generator.Generator = ®isterExternalGenerator{} + +func (g *registerExternalGenerator) Filter(_ *generator.Context, _ *types.Type) bool { + return false +} + +func (g *registerExternalGenerator) Imports(c *generator.Context) (imports []string) { + return g.imports.ImportLines() +} + +func (g *registerExternalGenerator) Namers(_ *generator.Context) namer.NameSystems { + return namer.NameSystems{ + "raw": namer.NewRawNamer(g.outputPackage, g.imports), + } +} + +func (g *registerExternalGenerator) Finalize(context *generator.Context, w io.Writer) error { + typesToGenerateOnlyNames := make([]string, len(g.typesToGenerate)) + for index, typeToGenerate := range g.typesToGenerate { + typesToGenerateOnlyNames[index] = typeToGenerate.Name.Name + } + + // sort the list of types to register, so that the generator produces stable output + sort.Strings(typesToGenerateOnlyNames) + + sw := generator.NewSnippetWriter(w, context, "$", "$") + m := map[string]interface{}{ + "groupName": g.gv.Group, + "version": g.gv.Version, + "types": typesToGenerateOnlyNames, + "addToGroupVersion": context.Universe.Function(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "AddToGroupVersion"}), + "groupVersion": context.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "GroupVersion"}), + } + sw.Do(registerExternalTypesTemplate, m) + return sw.Error() +} + +var registerExternalTypesTemplate = ` +// GroupName specifies the group name used to register the objects. +const GroupName = "$.groupName$" + +// GroupVersion specifies the group and the version used to register the objects. +var GroupVersion = $.groupVersion|raw${Group: GroupName, Version: "$.version$"} + +// SchemeGroupVersion is group version used to register these objects +// Deprecated: use GroupName instead. +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "$.version$"} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + // localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes. + SchemeBuilder runtime.SchemeBuilder + localSchemeBuilder = &SchemeBuilder + // Depreciated: use Install instead + AddToScheme = localSchemeBuilder.AddToScheme + Install = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addKnownTypes) +} + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + $range .types -$ + &$.${}, + $end$ + ) + // AddToGroupVersion allows the serialization of client types like ListOptions. + $.addToGroupVersion|raw$(scheme, SchemeGroupVersion) + return nil +} +` diff --git a/staging/src/k8s.io/code-generator/cmd/register-gen/main.go b/staging/src/k8s.io/code-generator/cmd/register-gen/main.go new file mode 100644 index 0000000000000..db02a4af4b5a5 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/register-gen/main.go @@ -0,0 +1,52 @@ +/* +Copyright 2018 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 main + +import ( + "flag" + "path/filepath" + + "github.com/golang/glog" + "github.com/spf13/pflag" + + generatorargs "k8s.io/code-generator/cmd/register-gen/args" + "k8s.io/code-generator/cmd/register-gen/generators" + "k8s.io/code-generator/pkg/util" + "k8s.io/gengo/args" +) + +func main() { + genericArgs := generatorargs.NewDefaults() + genericArgs.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), util.BoilerplatePath()) + genericArgs.AddFlags(pflag.CommandLine) + flag.Set("logtostderr", "true") + pflag.CommandLine.AddGoFlagSet(flag.CommandLine) + + pflag.Parse() + if err := generatorargs.Validate(genericArgs); err != nil { + glog.Fatalf("Error: %v", err) + } + + if err := genericArgs.Execute( + generators.NameSystems(), + generators.DefaultNameSystem(), + generators.Packages, + ); err != nil { + glog.Fatalf("Error: %v", err) + } + glog.V(2).Info("Completed successfully.") +} diff --git a/staging/src/k8s.io/code-generator/cmd/register-gen/runme.sh b/staging/src/k8s.io/code-generator/cmd/register-gen/runme.sh new file mode 100755 index 0000000000000..41dd2033eb99f --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/register-gen/runme.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +./main --v=6 --output-file-base=register --input-dirs k8s.io/api/admission/v1beta1,k8s.io/api/admissionregistration/v1alpha1,k8s.io/api/admissionregistration/v1beta1,k8s.io/api/apps/v1,k8s.io/api/apps/v1beta1,k8s.io/api/apps/v1beta2,k8s.io/api/authentication/v1,k8s.io/api/authentication/v1beta1,k8s.io/api/authorization/v1,k8s.io/api/authorization/v1beta1,k8s.io/api/autoscaling/v1,k8s.io/api/autoscaling/v2beta1,k8s.io/api/batch/v1,k8s.io/api/batch/v1beta1,k8s.io/api/batch/v2alpha1,k8s.io/api/certificates/v1beta1,k8s.io/api/coordination/v1beta1,k8s.io/api/core/v1,k8s.io/api/events/v1beta1,k8s.io/api/extensions/v1beta1,k8s.io/api/imagepolicy/v1alpha1,k8s.io/api/networking/v1,k8s.io/api/policy/v1beta1,k8s.io/api/rbac/v1,k8s.io/api/rbac/v1alpha1,k8s.io/api/rbac/v1beta1,k8s.io/api/scheduling/v1alpha1,k8s.io/api/scheduling/v1beta1,k8s.io/api/settings/v1alpha1,k8s.io/api/storage/v1,k8s.io/api/storage/v1alpha1,k8s.io/api/storage/v1beta1