prevent deprecated & removed from being generated for GA types

This commit is contained in:
Alexander Zielenski 2024-04-09 10:43:29 -07:00
parent 8d45bbea2b
commit 0095f607ab
2 changed files with 293 additions and 20 deletions

View File

@ -20,6 +20,7 @@ import (
"fmt"
"io"
"path"
"regexp"
"strconv"
"strings"
@ -364,6 +365,10 @@ func (g *genPreleaseLifecycle) Imports(c *generator.Context) (imports []string)
return importLines
}
var (
isGAVersionRegex = regexp.MustCompile(`^v\d+$`)
)
func (g *genPreleaseLifecycle) argsFromType(c *generator.Context, t *types.Type) (generator.Args, error) {
a := generator.Args{
"type": t,
@ -372,37 +377,53 @@ func (g *genPreleaseLifecycle) argsFromType(c *generator.Context, t *types.Type)
if err != nil {
return nil, err
}
// Take version from package last segment.
// Use heuristic to determine whether the package is GA or prerelease.
// If the package is GA, the version matches the format vN where N is a number.
version := path.Base(t.Name.Package)
isGAVersion := isGAVersionRegex.MatchString(version)
a = a.
With("introducedMajor", introducedMajor).
With("introducedMinor", introducedMinor)
// compute based on our policy
hasDeprecated := tagExists(deprecatedTagName, t)
hasRemoved := tagExists(removedTagName, t)
deprecatedMajor := introducedMajor
deprecatedMinor := introducedMinor + 3
// if someone intentionally override the deprecation release
if tagExists(deprecatedTagName, t) {
if hasDeprecated {
_, deprecatedMajor, deprecatedMinor, err = extractDeprecatedTag(t)
if err != nil {
return nil, err
}
}
a = a.
With("deprecatedMajor", deprecatedMajor).
With("deprecatedMinor", deprecatedMinor)
if !isGAVersion || hasDeprecated {
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) {
if hasRemoved {
_, removedMajor, removedMinor, err = extractRemovedTag(t)
if err != nil {
return nil, err
}
}
a = a.
With("removedMajor", removedMajor).
With("removedMinor", removedMinor)
if !isGAVersion || hasRemoved {
a = a.
With("removedMajor", removedMajor).
With("removedMinor", removedMinor)
}
replacementGroup, replacementVersion, replacementKind, hasReplacement, err := extractReplacementTag(t)
if err != nil {
@ -441,13 +462,17 @@ func (g *genPreleaseLifecycle) GenerateType(c *generator.Context, t *types.Type,
sw.Do(" return $.introducedMajor$, $.introducedMinor$\n", args)
sw.Do("}\n\n", nil)
}
if versionedMethodOrDie("APILifecycleDeprecated", t) == nil {
sw.Do("// APILifecycleDeprecated 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$) APILifecycleDeprecated() (major, minor int) {\n", args)
sw.Do(" return $.deprecatedMajor$, $.deprecatedMinor$\n", args)
sw.Do("}\n\n", nil)
if _, hasDeprecated := args["deprecatedMajor"]; hasDeprecated {
if versionedMethodOrDie("APILifecycleDeprecated", t) == nil {
sw.Do("// APILifecycleDeprecated 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$) APILifecycleDeprecated() (major, minor int) {\n", args)
sw.Do(" return $.deprecatedMajor$, $.deprecatedMinor$\n", args)
sw.Do("}\n\n", nil)
}
}
if _, hasReplacement := args["replacementKind"]; hasReplacement {
if versionedMethodOrDie("APILifecycleReplacement", t) == nil {
sw.Do("// APILifecycleReplacement is an autogenerated function, returning the group, version, and kind that should be used instead of this deprecated type.\n", args)
@ -457,12 +482,15 @@ func (g *genPreleaseLifecycle) GenerateType(c *generator.Context, t *types.Type,
sw.Do("}\n\n", nil)
}
}
if versionedMethodOrDie("APILifecycleRemoved", t) == nil {
sw.Do("// APILifecycleRemoved 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$) APILifecycleRemoved() (major, minor int) {\n", args)
sw.Do(" return $.removedMajor$, $.removedMinor$\n", args)
sw.Do("}\n\n", nil)
if _, hasRemoved := args["removedMajor"]; hasRemoved {
if versionedMethodOrDie("APILifecycleRemoved", t) == nil {
sw.Do("// APILifecycleRemoved 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$) APILifecycleRemoved() (major, minor int) {\n", args)
sw.Do(" return $.removedMajor$, $.removedMinor$\n", args)
sw.Do("}\n\n", nil)
}
}
return sw.Error()

View File

@ -20,8 +20,11 @@ import (
"fmt"
"reflect"
"strconv"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"k8s.io/gengo/v2/generator"
"k8s.io/gengo/v2/types"
"k8s.io/klog/v2"
)
@ -34,6 +37,248 @@ var mockType = &types.Type{
SecondClosestCommentLines: []string{},
}
func TestArgsFromType(t *testing.T) {
type testcase struct {
name string
t *types.Type
expected generator.Args
expectedError string
}
tests := []testcase{
{
name: "no comments",
t: &types.Type{
Name: types.Name{
Name: "Simple",
Package: "k8s.io/apis/core/v1",
},
},
expectedError: `missing`,
},
{
name: "GA type",
t: &types.Type{
Name: types.Name{
Name: "Simple",
Package: "k8s.io/apis/core/v1",
},
CommentLines: []string{
"+k8s:prerelease-lifecycle-gen:introduced=1.5",
},
},
expected: generator.Args{
"introducedMajor": 1,
"introducedMinor": 5,
},
},
{
name: "GA type v2",
t: &types.Type{
Name: types.Name{
Name: "Simple",
Package: "k8s.io/apis/core/v2",
},
CommentLines: []string{
"+k8s:prerelease-lifecycle-gen:introduced=1.5",
},
},
expected: generator.Args{
"introducedMajor": 1,
"introducedMinor": 5,
},
},
{
name: "GA type - explicit deprecated",
t: &types.Type{
Name: types.Name{
Name: "Simple",
Package: "k8s.io/apis/core/v1",
},
CommentLines: []string{
"+k8s:prerelease-lifecycle-gen:introduced=1.5",
"+k8s:prerelease-lifecycle-gen:deprecated=1.7",
},
},
expected: generator.Args{
"introducedMajor": 1,
"introducedMinor": 5,
"deprecatedMajor": 1,
"deprecatedMinor": 7,
},
},
{
name: "GA type - explicit removed",
t: &types.Type{
Name: types.Name{
Name: "Simple",
Package: "k8s.io/apis/core/v1",
},
CommentLines: []string{
"+k8s:prerelease-lifecycle-gen:introduced=1.5",
"+k8s:prerelease-lifecycle-gen:removed=1.9",
},
},
expected: generator.Args{
"introducedMajor": 1,
"introducedMinor": 5,
"removedMajor": 1,
"removedMinor": 9,
},
},
{
name: "GA type - explicit",
t: &types.Type{
Name: types.Name{
Name: "Simple",
Package: "k8s.io/apis/core/v1",
},
CommentLines: []string{
"+k8s:prerelease-lifecycle-gen:introduced=1.5",
"+k8s:prerelease-lifecycle-gen:deprecated=1.7",
"+k8s:prerelease-lifecycle-gen:removed=1.9",
},
},
expected: generator.Args{
"introducedMajor": 1,
"introducedMinor": 5,
"deprecatedMajor": 1,
"deprecatedMinor": 7,
"removedMajor": 1,
"removedMinor": 9,
},
},
{
name: "beta type - defaulted",
t: &types.Type{
Name: types.Name{
Name: "Simple",
Package: "k8s.io/apis/core/v1beta1",
},
CommentLines: []string{
"+k8s:prerelease-lifecycle-gen:introduced=1.5",
},
},
expected: generator.Args{
"introducedMajor": 1,
"introducedMinor": 5,
"deprecatedMajor": 1,
"deprecatedMinor": 8,
"removedMajor": 1,
"removedMinor": 11,
},
},
{
name: "beta type - explicit",
t: &types.Type{
Name: types.Name{
Name: "Simple",
Package: "k8s.io/apis/core/v1beta1",
},
CommentLines: []string{
"+k8s:prerelease-lifecycle-gen:introduced=1.5",
"+k8s:prerelease-lifecycle-gen:deprecated=1.7",
"+k8s:prerelease-lifecycle-gen:removed=1.9",
},
},
expected: generator.Args{
"introducedMajor": 1,
"introducedMinor": 5,
"deprecatedMajor": 1,
"deprecatedMinor": 7,
"removedMajor": 1,
"removedMinor": 9,
},
},
{
name: "beta type - explicit deprecated only",
t: &types.Type{
Name: types.Name{
Name: "Simple",
Package: "k8s.io/apis/core/v1beta1",
},
CommentLines: []string{
"+k8s:prerelease-lifecycle-gen:introduced=1.5",
"+k8s:prerelease-lifecycle-gen:deprecated=1.7",
},
},
expected: generator.Args{
"introducedMajor": 1,
"introducedMinor": 5,
"deprecatedMajor": 1,
"deprecatedMinor": 7,
"removedMajor": 1,
"removedMinor": 10,
},
},
{
name: "beta type - explicit removed only",
t: &types.Type{
Name: types.Name{
Name: "Simple",
Package: "k8s.io/apis/core/v1beta1",
},
CommentLines: []string{
"+k8s:prerelease-lifecycle-gen:introduced=1.5",
"+k8s:prerelease-lifecycle-gen:removed=1.9",
},
},
expected: generator.Args{
"introducedMajor": 1,
"introducedMinor": 5,
"deprecatedMajor": 1,
"deprecatedMinor": 8,
"removedMajor": 1,
"removedMinor": 9,
},
},
{
name: "alpha type - defaulted",
t: &types.Type{
Name: types.Name{
Name: "Simple",
Package: "k8s.io/apis/core/v1alpha1",
},
CommentLines: []string{
"+k8s:prerelease-lifecycle-gen:introduced=1.5",
},
},
expected: generator.Args{
"introducedMajor": 1,
"introducedMinor": 5,
"deprecatedMajor": 1,
"deprecatedMinor": 8,
"removedMajor": 1,
"removedMinor": 11,
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if test.expected != nil {
test.expected["type"] = test.t
}
gen := genPreleaseLifecycle{}
args, err := gen.argsFromType(nil, test.t)
if test.expectedError != "" {
if err == nil {
t.Errorf("expected error, got none")
} else if !strings.Contains(err.Error(), test.expectedError) {
t.Errorf("expected error %q, got %q", test.expectedError, err.Error())
}
return
}
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if diff := cmp.Diff(test.expected, args); diff != "" {
t.Error(diff)
}
})
}
}
func Test_extractKubeVersionTag(t *testing.T) {
oldKlogOsExit := klog.OsExit
defer func() {