Skip to content

Commit ea7d12d

Browse files
authored
Add generic deprecation warnings code for CRDs to find deprecated fields (#1196)
* Add generic deprecation warnings code for CRDs to find deprecated fields * Add license headers * fix linter issues * change some field names to be prefixed with deprecated * refactor deprecation warnings function for readability * fix up comment for clarity * only look at the spec field * ensure sorting even when field order changes
1 parent 71dada0 commit ea7d12d

File tree

18 files changed

+2018
-268
lines changed

18 files changed

+2018
-268
lines changed

gen/deprecations/cmd.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// Copyright 2025 Redpanda Data, Inc.
2+
//
3+
// Use of this software is governed by the Business Source License
4+
// included in the file licenses/BSL.md
5+
//
6+
// As of the Change Date specified in that file, in accordance with
7+
// the Business Source License, use of this software will be governed
8+
// by the Apache License, Version 2.0
9+
10+
package deprecations
11+
12+
import (
13+
_ "embed"
14+
"fmt"
15+
"go/ast"
16+
"os"
17+
"path/filepath"
18+
"strings"
19+
"text/template"
20+
"time"
21+
22+
"github.com/spf13/cobra"
23+
)
24+
25+
const DeprecationPrefix = "Deprecated"
26+
27+
var (
28+
testGenerator *template.Template
29+
//go:embed templates/test.tpl
30+
testTemplate string
31+
)
32+
33+
func init() {
34+
helpers := map[string]any{
35+
"year": func() string {
36+
return time.Now().Format("2006")
37+
},
38+
}
39+
testGenerator = template.Must(template.New("tests").Funcs(helpers).Parse(testTemplate))
40+
}
41+
42+
type DeprecationConfig struct {
43+
Directory string
44+
PackageName string
45+
OutputFile string
46+
Verbose bool
47+
}
48+
49+
type fieldRef struct {
50+
GoPath []string // Go field names e.g. [Spec ClusterSpec DeprecatedFullNameOverride]
51+
JsonPath []string // JSON names e.g. [spec clusterSpec fullNameOverride]
52+
TypeExpr ast.Expr
53+
}
54+
55+
type objSpec struct {
56+
Name string
57+
Literal string
58+
Warnings []string
59+
}
60+
61+
// Debugf emits debug output when the config's Verbose flag is set.
62+
func (c DeprecationConfig) Debugf(format string, a ...interface{}) {
63+
if !c.Verbose {
64+
return
65+
}
66+
fmt.Fprintf(os.Stderr, format, a...)
67+
}
68+
69+
func Cmd() *cobra.Command {
70+
var config DeprecationConfig
71+
72+
cmd := &cobra.Command{
73+
Use: "deprecations",
74+
Short: "Generate tests for deprecated API fields",
75+
Example: "gen deprecations --directory ./operator/api/redpanda/v1alpha2",
76+
RunE: func(cmd *cobra.Command, args []string) error {
77+
return Render(config)
78+
},
79+
}
80+
81+
cmd.Flags().StringVar(&config.Directory, "directory", ".", "The directory to scan for deprecated fields")
82+
cmd.Flags().StringVar(&config.PackageName, "package", "", "The name of the package, if not specified we try and figure it out dynamically")
83+
cmd.Flags().StringVar(&config.OutputFile, "output-file", "zz_generated.deprecations_test.go", "The name of the file to output in the given directory")
84+
cmd.Flags().BoolVarP(&config.Verbose, "verbose", "v", false, "Enable debug output.")
85+
86+
return cmd
87+
}
88+
89+
func Render(config DeprecationConfig) error {
90+
dir := config.Directory
91+
92+
if config.PackageName == "" {
93+
config.PackageName = strings.Trim(filepath.Base(dir), ".")
94+
config.PackageName = strings.Trim(filepath.Base(dir), "/")
95+
}
96+
97+
if config.PackageName == "" {
98+
return fmt.Errorf("could not determine package name")
99+
}
100+
101+
parser := NewParser(config)
102+
103+
if err := parser.Parse(); err != nil {
104+
return err
105+
}
106+
107+
contents, err := parser.Compile()
108+
if err != nil {
109+
return err
110+
}
111+
112+
outPath := filepath.Join(dir, config.OutputFile)
113+
if err := os.WriteFile(outPath, contents, 0o644); err != nil {
114+
return fmt.Errorf("failed to write output file: %w", err)
115+
}
116+
117+
fmt.Printf("wrote %s\n", outPath)
118+
119+
return nil
120+
}

0 commit comments

Comments
 (0)