-
Notifications
You must be signed in to change notification settings - Fork 13
/
main.go
265 lines (234 loc) · 8.95 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/*
Copyright 2022 The KCP 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 (
"encoding/json"
"fmt"
"io"
"os"
"strings"
"github.com/spf13/cobra"
"sigs.k8s.io/controller-tools/pkg/genall"
"sigs.k8s.io/controller-tools/pkg/genall/help"
prettyhelp "sigs.k8s.io/controller-tools/pkg/genall/help/pretty"
"sigs.k8s.io/controller-tools/pkg/markers"
"sigs.k8s.io/controller-tools/pkg/version"
"github.com/kcp-dev/code-generator/v2/pkg/generators/clientgen"
"github.com/kcp-dev/code-generator/v2/pkg/generators/informergen"
"github.com/kcp-dev/code-generator/v2/pkg/generators/listergen"
)
// Options are specified to controller-gen by turning generators and output rules into
// markers, and then parsing them using the standard registry logic (without the "+").
// Each marker and output rule should thus be usable as a marker target.
var (
// allGenerators maintains the list of all known generators, giving
// them names for use on the command line.
// each turns into a command line option,
// and has options for output forms.
allGenerators = map[string]genall.Generator{
"client": clientgen.Generator{},
"lister": listergen.Generator{},
"informer": informergen.Generator{},
}
// allOutputRules defines the list of all known output rules, giving
// them names for use on the command line.
// Each output rule turns into two command line options:
// - output:<generator>:<form> (per-generator output)
// - output:<form> (default output).
allOutputRules = map[string]genall.OutputRule{
"dir": genall.OutputToDirectory(""),
"none": genall.OutputToNothing,
"stdout": genall.OutputToStdout,
"artifacts": genall.OutputArtifacts{},
}
// optionsRegistry contains all the marker definitions used to process command line options.
optionsRegistry = &markers.Registry{}
)
func init() {
for genName, gen := range allGenerators {
// make the generator options marker itself.
def, err := markers.MakeDefinition(genName, markers.DescribesPackage, gen)
def.Strict = false
defn := markers.Must(def, err)
if err := optionsRegistry.Register(defn); err != nil {
panic(err)
}
if helpGiver, hasHelp := gen.(genall.HasHelp); hasHelp {
if help := helpGiver.Help(); help != nil {
optionsRegistry.AddHelp(defn, help)
}
}
// make per-generation output rule markers.
for ruleName, rule := range allOutputRules {
def, err := markers.MakeDefinition(fmt.Sprintf("output:%s:%s", genName, ruleName), markers.DescribesPackage, rule)
def.Strict = false
ruleMarker := markers.Must(def, err)
if err := optionsRegistry.Register(ruleMarker); err != nil {
panic(err)
}
if helpGiver, hasHelp := rule.(genall.HasHelp); hasHelp {
if help := helpGiver.Help(); help != nil {
optionsRegistry.AddHelp(ruleMarker, help)
}
}
}
}
// make "default output" output rule markers.
for ruleName, rule := range allOutputRules {
def, err := markers.MakeDefinition("output:"+ruleName, markers.DescribesPackage, rule)
def.Strict = false
ruleMarker := markers.Must(def, err)
if err := optionsRegistry.Register(ruleMarker); err != nil {
panic(err)
}
if helpGiver, hasHelp := rule.(genall.HasHelp); hasHelp {
if help := helpGiver.Help(); help != nil {
optionsRegistry.AddHelp(ruleMarker, help)
}
}
}
// add in the common options markers.
if err := genall.RegisterOptionsMarkers(optionsRegistry); err != nil {
panic(err)
}
}
// noUsageError suppresses usage printing when it occurs
// (since cobra doesn't provide a good way to avoid printing
// out usage in only certain situations).
type noUsageError struct{ error }
func main() {
helpLevel := 0
whichLevel := 0
showVersion := false
cmd := &cobra.Command{
Use: "code-generator",
Short: "Generate Cluster-Aware Kubernetes API clients, informers and listers.",
Long: "Generate Cluster-Aware Kubernetes API libraries.",
Example: ` # Generate listers for all types under apis/,
# outputting them to /tmp/clients
code-generator lister:apiPackagePath=acme.corp/pkg/apis,singleClusterListerPackagePath=acme.corp/pkg/generated/listers,headerFile=./../hack/boilerplate/boilerplate.go.txt paths=./apis/... output:dir=/tmp
# Run all the generators for a given project
code-generator \
"client:name=versioned,outputPackagePath=acme.corp/pkg/kcp,apiPackagePath=acme.corp/pkg/apis,singleClusterClientPackagePath=acme.corp/pkg/generated/clientset/versioned,headerFile=./../hack/boilerplate/boilerplate.go.txt,year=2022" \
"lister:apiPackagePath=acme.corp/pkg/apis,singleClusterListerPackagePath=acme.corp/pkg/generated/listers,headerFile=./../hack/boilerplate/boilerplate.go.txt,year=2022" \
"informer:outputPackagePath=acme.corp/pkg/kcp,apiPackagePath=acme.corp/pkg/apis,headerFile=./../hack/boilerplate/boilerplate.go.txt,year=2022" \
"paths=./pkg/apis/..." \
"output:dir=./pkg/kcpclients
# Explain the markers for generating listers, and their arguments
code-generator lister -ww
`,
RunE: func(c *cobra.Command, rawOpts []string) error {
// print version if asked for it.
if showVersion {
version.Print()
return nil
}
// print the help if we asked for it (since we've got a different help flag :-/), then bail.
if helpLevel > 0 {
return c.Usage()
}
// print the marker docs if we asked for them, then bail.
if whichLevel > 0 {
return printMarkerDocs(c, rawOpts, whichLevel)
}
// otherwise, set up the runtime for actually running the generators.
rt, err := genall.FromOptions(optionsRegistry, rawOpts)
if err != nil {
return err
}
if len(rt.Generators) == 0 {
return fmt.Errorf("no generators specified")
}
if hadErrs := rt.Run(); hadErrs {
// don't obscure the actual error with a bunch of usage
return noUsageError{fmt.Errorf("not all generators ran successfully")}
}
return nil
},
SilenceUsage: true, // silence the usage, then print it out ourselves if it wasn't suppressed.
}
cmd.Flags().CountVarP(&whichLevel, "which-markers", "w", "print out all markers available with the requested generators\n(up to -www for the most detailed output, or -wwww for json output)")
cmd.Flags().CountVarP(&helpLevel, "detailed-help", "h", "print out more detailed help\n(up to -hhh for the most detailed output, or -hhhh for json output)")
cmd.Flags().BoolVar(&showVersion, "version", false, "show version")
cmd.Flags().Bool("help", false, "print out usage and a summary of options")
oldUsage := cmd.UsageFunc()
cmd.SetUsageFunc(func(c *cobra.Command) error {
if err := oldUsage(c); err != nil {
return err
}
if helpLevel == 0 {
helpLevel = summaryHelp
}
fmt.Fprintf(c.OutOrStderr(), "\n\nOptions\n\n")
return helpForLevels(c.OutOrStdout(), c.OutOrStderr(), helpLevel, optionsRegistry, help.SortByOption)
})
if err := cmd.Execute(); err != nil {
if _, noUsage := err.(noUsageError); !noUsage {
// print the usage unless we suppressed it.
if err := cmd.Usage(); err != nil {
panic(err)
}
}
fmt.Fprintf(cmd.OutOrStderr(), "run `%[1]s %[2]s -w` to see all available markers, or `%[1]s %[2]s -h` for usage\n", cmd.CalledAs(), strings.Join(os.Args[1:], " "))
os.Exit(1)
}
}
// printMarkerDocs prints out marker help for the given generators specified in
// the rawOptions, at the given level.
func printMarkerDocs(c *cobra.Command, rawOptions []string, whichLevel int) error {
// just grab a registry so we don't lag while trying to load roots
// (like we'd do if we just constructed the full runtime).
reg, err := genall.RegistryFromOptions(optionsRegistry, rawOptions)
if err != nil {
return err
}
return helpForLevels(c.OutOrStdout(), c.OutOrStderr(), whichLevel, reg, help.SortByCategory)
}
func helpForLevels(mainOut io.Writer, errOut io.Writer, whichLevel int, reg *markers.Registry, sorter help.SortGroup) error {
helpInfo := help.ByCategory(reg, sorter)
switch whichLevel {
case jsonHelp:
if err := json.NewEncoder(mainOut).Encode(helpInfo); err != nil {
return err
}
case detailedHelp, fullHelp:
fullDetail := whichLevel == fullHelp
for _, cat := range helpInfo {
if cat.Category == "" {
continue
}
contents := prettyhelp.MarkersDetails(fullDetail, cat.Category, cat.Markers)
if err := contents.WriteTo(errOut); err != nil {
return err
}
}
case summaryHelp:
for _, cat := range helpInfo {
if cat.Category == "" {
continue
}
contents := prettyhelp.MarkersSummary(cat.Category, cat.Markers)
if err := contents.WriteTo(errOut); err != nil {
return err
}
}
}
return nil
}
const (
_ = iota
summaryHelp
detailedHelp
fullHelp
jsonHelp
)