mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
add prerelease-lifecycle generator for beta API types
This commit is contained in:
parent
88c5f64763
commit
4b0080d71f
@ -18,6 +18,7 @@ filegroup(
|
|||||||
"//staging/src/k8s.io/code-generator/cmd/informer-gen:all-srcs",
|
"//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/lister-gen:all-srcs",
|
||||||
"//staging/src/k8s.io/code-generator/cmd/openapi-gen:all-srcs",
|
"//staging/src/k8s.io/code-generator/cmd/openapi-gen:all-srcs",
|
||||||
|
"//staging/src/k8s.io/code-generator/cmd/prerelease-lifecycle-gen:all-srcs",
|
||||||
"//staging/src/k8s.io/code-generator/cmd/register-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/cmd/set-gen:all-srcs",
|
||||||
"//staging/src/k8s.io/code-generator/hack:all-srcs",
|
"//staging/src/k8s.io/code-generator/hack:all-srcs",
|
||||||
|
@ -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/prerelease-lifecycle-gen",
|
||||||
|
importpath = "k8s.io/code-generator/cmd/prerelease-lifecycle-gen",
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
deps = [
|
||||||
|
"//staging/src/k8s.io/code-generator/cmd/prerelease-lifecycle-gen/args:go_default_library",
|
||||||
|
"//staging/src/k8s.io/code-generator/cmd/prerelease-lifecycle-gen/prerelease-lifecycle-generators:go_default_library",
|
||||||
|
"//staging/src/k8s.io/code-generator/pkg/util:go_default_library",
|
||||||
|
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||||
|
"//vendor/k8s.io/gengo/args:go_default_library",
|
||||||
|
"//vendor/k8s.io/klog:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_binary(
|
||||||
|
name = "status-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/prerelease-lifecycle-gen/args:all-srcs",
|
||||||
|
"//staging/src/k8s.io/code-generator/cmd/prerelease-lifecycle-gen/prerelease-lifecycle-generators:all-srcs",
|
||||||
|
],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
@ -0,0 +1,28 @@
|
|||||||
|
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/prerelease-lifecycle-gen/args",
|
||||||
|
importpath = "k8s.io/code-generator/cmd/prerelease-lifecycle-gen/args",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//staging/src/k8s.io/code-generator/cmd/prerelease-lifecycle-gen/prerelease-lifecycle-generators:go_default_library",
|
||||||
|
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||||
|
"//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"],
|
||||||
|
)
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 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"
|
||||||
|
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
statusgenerators "k8s.io/code-generator/cmd/prerelease-lifecycle-gen/prerelease-lifecycle-generators"
|
||||||
|
"k8s.io/gengo/args"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CustomArgs is used by the gengo framework to pass args specific to this generator.
|
||||||
|
type CustomArgs statusgenerators.CustomArgs
|
||||||
|
|
||||||
|
// NewDefaults returns default arguments for the generator.
|
||||||
|
func NewDefaults() (*args.GeneratorArgs, *CustomArgs) {
|
||||||
|
genericArgs := args.Default().WithoutDefaultFlagParsing()
|
||||||
|
customArgs := &CustomArgs{}
|
||||||
|
genericArgs.CustomArgs = (*statusgenerators.CustomArgs)(customArgs) // convert to upstream type to make type-casts work there
|
||||||
|
genericArgs.OutputFileBaseName = "zz_prerelease_lifecycle_generated"
|
||||||
|
return genericArgs, customArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFlags add the generator flags to the flag set.
|
||||||
|
func (ca *CustomArgs) AddFlags(fs *pflag.FlagSet) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate checks the given arguments.
|
||||||
|
func Validate(genericArgs *args.GeneratorArgs) error {
|
||||||
|
_ = genericArgs.CustomArgs.(*statusgenerators.CustomArgs)
|
||||||
|
|
||||||
|
if len(genericArgs.OutputFileBaseName) == 0 {
|
||||||
|
return fmt.Errorf("output file base name cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// prerelease-lifecycle-gen is a tool for auto-generating api-status.csv files.
|
||||||
|
//
|
||||||
|
// Given a list of input directories, it will create a zz_api_status.go file for all beta APIs which indicates the kinds,
|
||||||
|
// the release it was introduced, the release it will be deprecated, and the release it will be removed.
|
||||||
|
//
|
||||||
|
// Generation is governed by comment tags in the source. Any package may
|
||||||
|
// request Status generation by including a comment in the file-comments of
|
||||||
|
// one file, of the form:
|
||||||
|
// // +k8s:prerelease-lifecycle-gen=package
|
||||||
|
//
|
||||||
|
// // +k8s:prerelease-lifecycle-gen:introduced=1.19
|
||||||
|
// // +k8s:prerelease-lifecycle-gen:to-be-deprecated=1.22
|
||||||
|
// // +k8s:prerelease-lifecycle-gen:to-be-removed=1.25
|
||||||
|
//
|
||||||
|
// Note that registration is a whole-package option, and is not available for
|
||||||
|
// individual types.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
generatorargs "k8s.io/code-generator/cmd/prerelease-lifecycle-gen/args"
|
||||||
|
statusgenerators "k8s.io/code-generator/cmd/prerelease-lifecycle-gen/prerelease-lifecycle-generators"
|
||||||
|
"k8s.io/code-generator/pkg/util"
|
||||||
|
"k8s.io/gengo/args"
|
||||||
|
"k8s.io/klog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
klog.InitFlags(nil)
|
||||||
|
genericArgs, customArgs := generatorargs.NewDefaults()
|
||||||
|
|
||||||
|
// Override defaults.
|
||||||
|
// TODO: move this out of prerelease-lifecycle-gen
|
||||||
|
genericArgs.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), util.BoilerplatePath())
|
||||||
|
|
||||||
|
genericArgs.AddFlags(pflag.CommandLine)
|
||||||
|
customArgs.AddFlags(pflag.CommandLine)
|
||||||
|
flag.Set("logtostderr", "true")
|
||||||
|
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
|
||||||
|
pflag.Parse()
|
||||||
|
|
||||||
|
if err := generatorargs.Validate(genericArgs); err != nil {
|
||||||
|
klog.Fatalf("Error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run it.
|
||||||
|
if err := genericArgs.Execute(
|
||||||
|
statusgenerators.NameSystems(),
|
||||||
|
statusgenerators.DefaultNameSystem(),
|
||||||
|
statusgenerators.Packages,
|
||||||
|
); err != nil {
|
||||||
|
klog.Fatalf("Error: %v", err)
|
||||||
|
}
|
||||||
|
klog.V(2).Info("Completed successfully.")
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["status.go"],
|
||||||
|
importmap = "k8s.io/kubernetes/vendor/k8s.io/code-generator/cmd/prerelease-lifecycle-gen/prerelease-lifecycle-generators",
|
||||||
|
importpath = "k8s.io/code-generator/cmd/prerelease-lifecycle-gen/prerelease-lifecycle-generators",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//vendor/k8s.io/gengo/args:go_default_library",
|
||||||
|
"//vendor/k8s.io/gengo/examples/set-gen/sets: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",
|
||||||
|
"//vendor/k8s.io/klog: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"],
|
||||||
|
)
|
@ -0,0 +1,438 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 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 prereleaselifecyclegenerators
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"k8s.io/gengo/args"
|
||||||
|
"k8s.io/gengo/examples/set-gen/sets"
|
||||||
|
"k8s.io/gengo/generator"
|
||||||
|
"k8s.io/gengo/namer"
|
||||||
|
"k8s.io/gengo/types"
|
||||||
|
|
||||||
|
"k8s.io/klog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CustomArgs is used tby the go2idl framework to pass args specific to this generator.
|
||||||
|
type CustomArgs struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the comment tag that carries parameters for API status generation. Because the cadence is fixed, we can predict
|
||||||
|
// with near certainty when this lifecycle happens as the API is introduced.
|
||||||
|
const (
|
||||||
|
tagEnabledName = "k8s:prerelease-lifecycle-gen"
|
||||||
|
introducedTagName = tagEnabledName + ":introduced"
|
||||||
|
deprecatedTagName = tagEnabledName + ":deprecated"
|
||||||
|
removedTagName = tagEnabledName + ":removed"
|
||||||
|
)
|
||||||
|
|
||||||
|
// enabledTagValue holds parameters from a tagName tag.
|
||||||
|
type tagValue struct {
|
||||||
|
value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractEnabledTypeTag(t *types.Type) *tagValue {
|
||||||
|
comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...)
|
||||||
|
return extractTag(tagEnabledName, comments)
|
||||||
|
}
|
||||||
|
|
||||||
|
func tagExists(tagName string, t *types.Type) bool {
|
||||||
|
comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...)
|
||||||
|
rawTag := extractTag(tagName, comments)
|
||||||
|
return rawTag != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractKubeVersionTag(tagName string, t *types.Type) (*tagValue, int64, int64, error) {
|
||||||
|
comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...)
|
||||||
|
rawTag := extractTag(tagName, comments)
|
||||||
|
if rawTag == nil || len(rawTag.value) == 0 {
|
||||||
|
return nil, -1, -1, fmt.Errorf("%v missing %v=Version tag", t, tagName)
|
||||||
|
}
|
||||||
|
|
||||||
|
splitValue := strings.Split(rawTag.value, ".")
|
||||||
|
if len(splitValue) != 2 || len(splitValue[0]) == 0 || len(splitValue[0]) == 0 {
|
||||||
|
return nil, -1, -1, fmt.Errorf("%v format must match %v=xx.yy tag", t, tagName)
|
||||||
|
}
|
||||||
|
major, err := strconv.ParseInt(splitValue[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, -1, -1, fmt.Errorf("%v format must match %v=xx.yy : %w", t, tagName, err)
|
||||||
|
}
|
||||||
|
minor, err := strconv.ParseInt(splitValue[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, -1, -1, fmt.Errorf("%v format must match %v=xx.yy : %w", t, tagName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawTag, major, minor, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractIntroducedTag(t *types.Type) (*tagValue, int64, int64, error) {
|
||||||
|
return extractKubeVersionTag(introducedTagName, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractDeprecatedTag(t *types.Type) (*tagValue, int64, int64, error) {
|
||||||
|
return extractKubeVersionTag(deprecatedTagName, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractRemovedTag(t *types.Type) (*tagValue, int64, int64, error) {
|
||||||
|
return extractKubeVersionTag(removedTagName, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractTag(tagName string, comments []string) *tagValue {
|
||||||
|
tagVals := types.ExtractCommentTags("+", comments)[tagName]
|
||||||
|
if tagVals == nil {
|
||||||
|
// No match for the tag.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// If there are multiple values, abort.
|
||||||
|
if len(tagVals) > 1 {
|
||||||
|
klog.Fatalf("Found %d %s tags: %q", len(tagVals), tagName, tagVals)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got here we are returning something.
|
||||||
|
tag := &tagValue{}
|
||||||
|
|
||||||
|
// Get the primary value.
|
||||||
|
parts := strings.Split(tagVals[0], ",")
|
||||||
|
if len(parts) >= 1 {
|
||||||
|
tag.value = parts[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse extra arguments.
|
||||||
|
parts = parts[1:]
|
||||||
|
for i := range parts {
|
||||||
|
kv := strings.SplitN(parts[i], "=", 2)
|
||||||
|
k := kv[0]
|
||||||
|
//v := ""
|
||||||
|
//if len(kv) == 2 {
|
||||||
|
// v = kv[1]
|
||||||
|
//}
|
||||||
|
switch k {
|
||||||
|
//case "register":
|
||||||
|
// if v != "false" {
|
||||||
|
// tag.register = true
|
||||||
|
// }
|
||||||
|
default:
|
||||||
|
klog.Fatalf("Unsupported %s param: %q", tagName, parts[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tag
|
||||||
|
}
|
||||||
|
|
||||||
|
// NameSystems returns the name system used by the generators in this package.
|
||||||
|
func NameSystems() namer.NameSystems {
|
||||||
|
return namer.NameSystems{
|
||||||
|
"public": namer.NewPublicNamer(1),
|
||||||
|
"raw": namer.NewRawNamer("", nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultNameSystem returns the default name system for ordering the types to be
|
||||||
|
// processed by the generators in this package.
|
||||||
|
func DefaultNameSystem() string {
|
||||||
|
return "public"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Packages makes the package definition.
|
||||||
|
func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
|
||||||
|
boilerplate, err := arguments.LoadGoBoilerplate()
|
||||||
|
if err != nil {
|
||||||
|
klog.Fatalf("Failed loading boilerplate: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs := sets.NewString(context.Inputs...)
|
||||||
|
packages := generator.Packages{}
|
||||||
|
header := append([]byte(fmt.Sprintf("// +build !%s\n\n", arguments.GeneratedBuildTag)), boilerplate...)
|
||||||
|
|
||||||
|
for i := range inputs {
|
||||||
|
klog.V(5).Infof("Considering pkg %q", i)
|
||||||
|
pkg := context.Universe[i]
|
||||||
|
if pkg == nil {
|
||||||
|
// If the input had no Go files, for example.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ptag := extractTag(tagEnabledName, pkg.Comments)
|
||||||
|
pkgNeedsGeneration := false
|
||||||
|
if ptag != nil {
|
||||||
|
pkgNeedsGeneration, err = strconv.ParseBool(ptag.value)
|
||||||
|
if err != nil {
|
||||||
|
klog.Fatalf("Package %v: unsupported %s value: %q :%w", i, tagEnabledName, ptag.value, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !pkgNeedsGeneration {
|
||||||
|
klog.V(5).Infof(" skipping package")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
klog.Infof("Generating package %q", pkg.Path)
|
||||||
|
|
||||||
|
// If the pkg-scoped tag says to generate, we can skip scanning types.
|
||||||
|
if !pkgNeedsGeneration {
|
||||||
|
// If the pkg-scoped tag did not exist, scan all types for one that
|
||||||
|
// explicitly wants generation.
|
||||||
|
for _, t := range pkg.Types {
|
||||||
|
klog.V(5).Infof(" considering type %q", t.Name.String())
|
||||||
|
ttag := extractEnabledTypeTag(t)
|
||||||
|
if ttag != nil && ttag.value == "true" {
|
||||||
|
klog.V(5).Infof(" tag=true")
|
||||||
|
if !isAPIType(t) {
|
||||||
|
klog.Fatalf("Type %v requests deepcopy generation but is not copyable", t)
|
||||||
|
}
|
||||||
|
pkgNeedsGeneration = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pkgNeedsGeneration {
|
||||||
|
path := pkg.Path
|
||||||
|
// if the source path is within a /vendor/ directory (for example,
|
||||||
|
// k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/apis/meta/v1), allow
|
||||||
|
// generation to output to the proper relative path (under vendor).
|
||||||
|
// Otherwise, the generator will create the file in the wrong location
|
||||||
|
// in the output directory.
|
||||||
|
// TODO: build a more fundamental concept in gengo for dealing with modifications
|
||||||
|
// to vendored packages.
|
||||||
|
if strings.HasPrefix(pkg.SourcePath, arguments.OutputBase) {
|
||||||
|
expandedPath := strings.TrimPrefix(pkg.SourcePath, arguments.OutputBase)
|
||||||
|
if strings.Contains(expandedPath, "/vendor/") {
|
||||||
|
path = expandedPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
packages = append(packages,
|
||||||
|
&generator.DefaultPackage{
|
||||||
|
PackageName: strings.Split(filepath.Base(pkg.Path), ".")[0],
|
||||||
|
PackagePath: path,
|
||||||
|
HeaderText: header,
|
||||||
|
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
|
||||||
|
return []generator.Generator{
|
||||||
|
NewPrereleaseLifecycleGen(arguments.OutputFileBaseName, pkg.Path),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FilterFunc: func(c *generator.Context, t *types.Type) bool {
|
||||||
|
return t.Name.Package == pkg.Path
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return packages
|
||||||
|
}
|
||||||
|
|
||||||
|
// genDeepCopy produces a file with autogenerated deep-copy functions.
|
||||||
|
type genPreleaseLifecycle struct {
|
||||||
|
generator.DefaultGen
|
||||||
|
targetPackage string
|
||||||
|
imports namer.ImportTracker
|
||||||
|
typesForInit []*types.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPrereleaseLifecycleGen creates a generator for the prerelease-lifecycle-generator
|
||||||
|
func NewPrereleaseLifecycleGen(sanitizedName, targetPackage string) generator.Generator {
|
||||||
|
return &genPreleaseLifecycle{
|
||||||
|
DefaultGen: generator.DefaultGen{
|
||||||
|
OptionalName: sanitizedName,
|
||||||
|
},
|
||||||
|
targetPackage: targetPackage,
|
||||||
|
imports: generator.NewImportTracker(),
|
||||||
|
typesForInit: make([]*types.Type, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *genPreleaseLifecycle) Namers(c *generator.Context) namer.NameSystems {
|
||||||
|
return namer.NameSystems{
|
||||||
|
"public": namer.NewPublicNamer(1),
|
||||||
|
"intrapackage": namer.NewPublicNamer(0),
|
||||||
|
"raw": namer.NewRawNamer("", nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *genPreleaseLifecycle) Filter(c *generator.Context, t *types.Type) bool {
|
||||||
|
// Filter out types not being processed or not copyable within the package.
|
||||||
|
if !isAPIType(t) {
|
||||||
|
klog.V(2).Infof("Type %v is not a valid target for status", t)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
g.typesForInit = append(g.typesForInit, t)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// versionMethod returns the signature of an <methodName>() method, nil or an error
|
||||||
|
// if the type is wrong. Introduced() allows more efficient deep copy
|
||||||
|
// implementations to be defined by the type's author. The correct signature
|
||||||
|
// func (t *T) <methodName>() string
|
||||||
|
func versionMethod(methodName string, t *types.Type) (*types.Signature, error) {
|
||||||
|
f, found := t.Methods[methodName]
|
||||||
|
if !found {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if len(f.Signature.Parameters) != 0 {
|
||||||
|
return nil, fmt.Errorf("type %v: invalid %v signature, expected no parameters", t, methodName)
|
||||||
|
}
|
||||||
|
if len(f.Signature.Results) != 2 {
|
||||||
|
return nil, fmt.Errorf("type %v: invalid %v signature, expected exactly two result types", t, methodName)
|
||||||
|
}
|
||||||
|
|
||||||
|
ptrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Kind == types.Pointer && f.Signature.Receiver.Elem.Name == t.Name
|
||||||
|
nonPtrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Name == t.Name
|
||||||
|
|
||||||
|
if !ptrRcvr && !nonPtrRcvr {
|
||||||
|
// this should never happen
|
||||||
|
return nil, fmt.Errorf("type %v: invalid %v signature, expected a receiver of type %s or *%s", t, methodName, t.Name.Name, t.Name.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.Signature, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// versionedMethodOrDie returns the signature of a <methodName>() method, nil or calls klog.Fatalf
|
||||||
|
// if the type is wrong.
|
||||||
|
func versionedMethodOrDie(methodName string, t *types.Type) *types.Signature {
|
||||||
|
ret, err := versionMethod(methodName, t)
|
||||||
|
if err != nil {
|
||||||
|
klog.Fatal(err)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// isAPIType indicates whether or not a type could be used to serve an API. That means, "does it have TypeMeta".
|
||||||
|
// This doesn't mean the type is served, but we will handle all TypeMeta types.
|
||||||
|
func isAPIType(t *types.Type) bool {
|
||||||
|
// Filter out private types.
|
||||||
|
if namer.IsPrivateGoName(t.Name.Name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Kind != types.Struct {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, currMember := range t.Members {
|
||||||
|
if currMember.Embedded && currMember.Name == "TypeMeta" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Kind == types.Alias {
|
||||||
|
return isAPIType(t.Underlying)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *genPreleaseLifecycle) isOtherPackage(pkg string) bool {
|
||||||
|
if pkg == g.targetPackage {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(pkg, "\""+g.targetPackage+"\"") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *genPreleaseLifecycle) Imports(c *generator.Context) (imports []string) {
|
||||||
|
importLines := []string{}
|
||||||
|
for _, singleImport := range g.imports.ImportLines() {
|
||||||
|
if g.isOtherPackage(singleImport) {
|
||||||
|
importLines = append(importLines, singleImport)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return importLines
|
||||||
|
}
|
||||||
|
|
||||||
|
func argsFromType(t *types.Type) (generator.Args, error) {
|
||||||
|
a := generator.Args{
|
||||||
|
"type": t,
|
||||||
|
}
|
||||||
|
_, introducedMajor, introducedMinor, err := extractIntroducedTag(t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
a = a.
|
||||||
|
With("introducedMajor", introducedMajor).
|
||||||
|
With("introducedMinor", introducedMinor)
|
||||||
|
|
||||||
|
// compute based on our policy
|
||||||
|
deprecatedMajor := introducedMajor
|
||||||
|
deprecatedMinor := introducedMinor + 3
|
||||||
|
// if someone intentionally override the deprecation release
|
||||||
|
if tagExists(deprecatedTagName, t) {
|
||||||
|
_, deprecatedMajor, deprecatedMinor, err = extractDeprecatedTag(t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a = a.
|
||||||
|
With("deprecatedMajor", deprecatedMajor).
|
||||||
|
With("deprecatedMinor", deprecatedMinor)
|
||||||
|
|
||||||
|
// compute based on our policy
|
||||||
|
removedMajor := deprecatedMajor
|
||||||
|
removedMinor := deprecatedMinor + 3
|
||||||
|
// if someone intentionally override the removed release
|
||||||
|
if tagExists(removedTagName, t) {
|
||||||
|
_, removedMajor, removedMinor, err = extractRemovedTag(t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a = a.
|
||||||
|
With("removedMajor", removedMajor).
|
||||||
|
With("removedMinor", removedMinor)
|
||||||
|
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *genPreleaseLifecycle) Init(c *generator.Context, w io.Writer) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *genPreleaseLifecycle) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
|
||||||
|
klog.Infof("Generating prerelease-lifecycle for type %v", t)
|
||||||
|
|
||||||
|
sw := generator.NewSnippetWriter(w, c, "$", "$")
|
||||||
|
args, err := argsFromType(t)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if versionedMethodOrDie("Introduced", t) == nil {
|
||||||
|
sw.Do("// Introduced is an autogenerated function, returning the release in which the API struct was introduced as int versions of major and minor for comparison.\n", args)
|
||||||
|
sw.Do("// It is controlled by \""+introducedTagName+"\" tags in types.go.\n", args)
|
||||||
|
sw.Do("func (in *$.type|intrapackage$) Introduced() (int64, int64) {\n", args)
|
||||||
|
sw.Do(" return $.introducedMajor$, $.introducedMinor$\n", args)
|
||||||
|
sw.Do("}\n\n", nil)
|
||||||
|
}
|
||||||
|
if versionedMethodOrDie("Deprecated", t) == nil {
|
||||||
|
sw.Do("// Deprecated is an autogenerated function, returning the release in which the API struct was or will be deprecated as int versions of major and minor for comparison.\n", args)
|
||||||
|
sw.Do("// It is controlled by \""+deprecatedTagName+"\" tags in types.go or \""+introducedTagName+"\" plus three minor.\n", args)
|
||||||
|
sw.Do("func (in *$.type|intrapackage$) Deprecated() (int64, int64) {\n", args)
|
||||||
|
sw.Do(" return $.deprecatedMajor$, $.deprecatedMinor$\n", args)
|
||||||
|
sw.Do("}\n\n", nil)
|
||||||
|
}
|
||||||
|
if versionedMethodOrDie("Removed", t) == nil {
|
||||||
|
sw.Do("// Removed is an autogenerated function, returning the release in which the API is no longer served as int versions of major and minor for comparison.\n", args)
|
||||||
|
sw.Do("// It is controlled by \""+removedTagName+"\" tags in types.go or \""+deprecatedTagName+"\" plus three minor.\n", args)
|
||||||
|
sw.Do("func (in *$.type|intrapackage$) Removed() (int64, int64) {\n", args)
|
||||||
|
sw.Do(" return $.removedMajor$, $.removedMinor$\n", args)
|
||||||
|
sw.Do("}\n\n", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sw.Error()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user