Prerelease lifecycle generator updates

Logs at lower verbosity to match other generators

Switches major/minor return types to int

Prefixes generated method names with APILifecycle to avoid collisions

Adds a k8s:prerelease-lifecycle-gen:replacement=group,version,kind tag for indicating a replacement API
This commit is contained in:
Jordan Liggitt 2020-04-17 12:28:20 -04:00
parent 875d3b534e
commit d0babe2613

View File

@ -43,6 +43,8 @@ const (
introducedTagName = tagEnabledName + ":introduced" introducedTagName = tagEnabledName + ":introduced"
deprecatedTagName = tagEnabledName + ":deprecated" deprecatedTagName = tagEnabledName + ":deprecated"
removedTagName = tagEnabledName + ":removed" removedTagName = tagEnabledName + ":removed"
replacementTagName = tagEnabledName + ":replacement"
) )
// enabledTagValue holds parameters from a tagName tag. // enabledTagValue holds parameters from a tagName tag.
@ -61,7 +63,7 @@ func tagExists(tagName string, t *types.Type) bool {
return rawTag != nil return rawTag != nil
} }
func extractKubeVersionTag(tagName string, t *types.Type) (*tagValue, int64, int64, error) { func extractKubeVersionTag(tagName string, t *types.Type) (*tagValue, int, int, error) {
comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...) comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...)
rawTag := extractTag(tagName, comments) rawTag := extractTag(tagName, comments)
if rawTag == nil || len(rawTag.value) == 0 { if rawTag == nil || len(rawTag.value) == 0 {
@ -72,30 +74,66 @@ func extractKubeVersionTag(tagName string, t *types.Type) (*tagValue, int64, int
if len(splitValue) != 2 || len(splitValue[0]) == 0 || len(splitValue[0]) == 0 { 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) return nil, -1, -1, fmt.Errorf("%v format must match %v=xx.yy tag", t, tagName)
} }
major, err := strconv.ParseInt(splitValue[0], 10, 64) major, err := strconv.ParseInt(splitValue[0], 10, 32)
if err != nil { if err != nil {
return nil, -1, -1, fmt.Errorf("%v format must match %v=xx.yy : %w", t, tagName, err) 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) minor, err := strconv.ParseInt(splitValue[1], 10, 32)
if err != nil { if err != nil {
return nil, -1, -1, fmt.Errorf("%v format must match %v=xx.yy : %w", t, tagName, err) return nil, -1, -1, fmt.Errorf("%v format must match %v=xx.yy : %w", t, tagName, err)
} }
return rawTag, major, minor, nil return rawTag, int(major), int(minor), nil
} }
func extractIntroducedTag(t *types.Type) (*tagValue, int64, int64, error) { func extractIntroducedTag(t *types.Type) (*tagValue, int, int, error) {
return extractKubeVersionTag(introducedTagName, t) return extractKubeVersionTag(introducedTagName, t)
} }
func extractDeprecatedTag(t *types.Type) (*tagValue, int64, int64, error) { func extractDeprecatedTag(t *types.Type) (*tagValue, int, int, error) {
return extractKubeVersionTag(deprecatedTagName, t) return extractKubeVersionTag(deprecatedTagName, t)
} }
func extractRemovedTag(t *types.Type) (*tagValue, int64, int64, error) { func extractRemovedTag(t *types.Type) (*tagValue, int, int, error) {
return extractKubeVersionTag(removedTagName, t) return extractKubeVersionTag(removedTagName, t)
} }
func extractReplacementTag(t *types.Type) (group, version, kind string, hasReplacement bool, err error) {
comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...)
tagVals := types.ExtractCommentTags("+", comments)[replacementTagName]
if len(tagVals) == 0 {
// No match for the tag.
return "", "", "", false, nil
}
// If there are multiple values, abort.
if len(tagVals) > 1 {
return "", "", "", false, fmt.Errorf("Found %d %s tags: %q", len(tagVals), replacementTagName, tagVals)
}
tagValue := tagVals[0]
parts := strings.Split(tagValue, ",")
if len(parts) != 3 {
return "", "", "", false, fmt.Errorf(`%s value must be "<group>,<version>,<kind>", got %q`, replacementTagName, tagValue)
}
group, version, kind = parts[0], parts[1], parts[2]
if len(version) == 0 || len(kind) == 0 {
return "", "", "", false, fmt.Errorf(`%s value must be "<group>,<version>,<kind>", got %q`, replacementTagName, tagValue)
}
// sanity check the group
if strings.ToLower(group) != group {
return "", "", "", false, fmt.Errorf(`replacement group must be all lower-case, got %q`, group)
}
// sanity check the version
if !strings.HasPrefix(version, "v") || strings.ToLower(version) != version {
return "", "", "", false, fmt.Errorf(`replacement version must start with "v" and be all lower-case, got %q`, version)
}
// sanity check the kind
if strings.ToUpper(kind[:1]) != kind[:1] {
return "", "", "", false, fmt.Errorf(`replacement kind must start with uppercase-letter, got %q`, kind)
}
return group, version, kind, true, nil
}
func extractTag(tagName string, comments []string) *tagValue { func extractTag(tagName string, comments []string) *tagValue {
tagVals := types.ExtractCommentTags("+", comments)[tagName] tagVals := types.ExtractCommentTags("+", comments)[tagName]
if tagVals == nil { if tagVals == nil {
@ -182,7 +220,7 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
klog.V(5).Infof(" skipping package") klog.V(5).Infof(" skipping package")
continue continue
} }
klog.Infof("Generating package %q", pkg.Path) klog.V(3).Infof("Generating package %q", pkg.Path)
// If the pkg-scoped tag says to generate, we can skip scanning types. // If the pkg-scoped tag says to generate, we can skip scanning types.
if !pkgNeedsGeneration { if !pkgNeedsGeneration {
@ -356,7 +394,7 @@ func (g *genPreleaseLifecycle) Imports(c *generator.Context) (imports []string)
return importLines return importLines
} }
func argsFromType(t *types.Type) (generator.Args, error) { func (g *genPreleaseLifecycle) argsFromType(c *generator.Context, t *types.Type) (generator.Args, error) {
a := generator.Args{ a := generator.Args{
"type": t, "type": t,
} }
@ -396,6 +434,20 @@ func argsFromType(t *types.Type) (generator.Args, error) {
With("removedMajor", removedMajor). With("removedMajor", removedMajor).
With("removedMinor", removedMinor) With("removedMinor", removedMinor)
replacementGroup, replacementVersion, replacementKind, hasReplacement, err := extractReplacementTag(t)
if err != nil {
return nil, err
}
if hasReplacement {
gvkType := c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/runtime/schema", Name: "GroupVersionKind"})
g.imports.AddType(gvkType)
a = a.
With("replacementGroup", replacementGroup).
With("replacementVersion", replacementVersion).
With("replacementKind", replacementKind).
With("GroupVersionKind", gvkType)
}
return a, nil return a, nil
} }
@ -404,32 +456,41 @@ func (g *genPreleaseLifecycle) Init(c *generator.Context, w io.Writer) error {
} }
func (g *genPreleaseLifecycle) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { func (g *genPreleaseLifecycle) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
klog.Infof("Generating prerelease-lifecycle for type %v", t) klog.V(3).Infof("Generating prerelease-lifecycle for type %v", t)
sw := generator.NewSnippetWriter(w, c, "$", "$") sw := generator.NewSnippetWriter(w, c, "$", "$")
args, err := argsFromType(t) args, err := g.argsFromType(c, t)
if err != nil { if err != nil {
return err return err
} }
if versionedMethodOrDie("Introduced", t) == nil { if versionedMethodOrDie("APILifecycleIntroduced", 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("// APILifecycleIntroduced 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("// It is controlled by \""+introducedTagName+"\" tags in types.go.\n", args)
sw.Do("func (in *$.type|intrapackage$) Introduced() (int64, int64) {\n", args) sw.Do("func (in *$.type|intrapackage$) APILifecycleIntroduced() (major, minor int) {\n", args)
sw.Do(" return $.introducedMajor$, $.introducedMinor$\n", args) sw.Do(" return $.introducedMajor$, $.introducedMinor$\n", args)
sw.Do("}\n\n", nil) sw.Do("}\n\n", nil)
} }
if versionedMethodOrDie("Deprecated", t) == nil { if versionedMethodOrDie("APILifecycleDeprecated", 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("// 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("// 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("func (in *$.type|intrapackage$) APILifecycleDeprecated() (major, minor int) {\n", args)
sw.Do(" return $.deprecatedMajor$, $.deprecatedMinor$\n", args) sw.Do(" return $.deprecatedMajor$, $.deprecatedMinor$\n", args)
sw.Do("}\n\n", nil) sw.Do("}\n\n", nil)
} }
if versionedMethodOrDie("Removed", t) == nil { if _, hasReplacement := args["replacementKind"]; hasReplacement {
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) 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)
sw.Do("// It is controlled by \""+replacementTagName+"=<group>,<version>,<kind>\" tags in types.go.\n", args)
sw.Do("func (in *$.type|intrapackage$) APILifecycleReplacement() ($.GroupVersionKind|raw$) {\n", args)
sw.Do(" return $.GroupVersionKind|raw${Group:\"$.replacementGroup$\", Version:\"$.replacementVersion$\", Kind:\"$.replacementKind$\"}\n", args)
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("// 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("func (in *$.type|intrapackage$) APILifecycleRemoved() (major, minor int) {\n", args)
sw.Do(" return $.removedMajor$, $.removedMinor$\n", args) sw.Do(" return $.removedMajor$, $.removedMinor$\n", args)
sw.Do("}\n\n", nil) sw.Do("}\n\n", nil)
} }