github.com/matthchr/controller-tools@v0.3.1-0.20200602225425-d33ced351ff8/cmd/controller-gen/main.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 package main 17 18 import ( 19 "encoding/json" 20 "fmt" 21 "io" 22 "os" 23 "strings" 24 25 "github.com/spf13/cobra" 26 27 "github.com/matthchr/controller-tools/pkg/crd" 28 "github.com/matthchr/controller-tools/pkg/deepcopy" 29 "github.com/matthchr/controller-tools/pkg/genall" 30 "github.com/matthchr/controller-tools/pkg/genall/help" 31 prettyhelp "github.com/matthchr/controller-tools/pkg/genall/help/pretty" 32 "github.com/matthchr/controller-tools/pkg/markers" 33 "github.com/matthchr/controller-tools/pkg/rbac" 34 "github.com/matthchr/controller-tools/pkg/schemapatcher" 35 "github.com/matthchr/controller-tools/pkg/version" 36 "github.com/matthchr/controller-tools/pkg/webhook" 37 ) 38 39 //go:generate go run ../helpgen/main.go paths=../../pkg/... generate:headerFile=../../boilerplate.go.txt,year=2019 40 41 // Options are specified to controller-gen by turning generators and output rules into 42 // markers, and then parsing them using the standard registry logic (without the "+"). 43 // Each marker and output rule should thus be usable as a marker target. 44 45 var ( 46 // allGenerators maintains the list of all known generators, giving 47 // them names for use on the command line. 48 // each turns into a command line option, 49 // and has options for output forms. 50 allGenerators = map[string]genall.Generator{ 51 "crd": crd.Generator{}, 52 "rbac": rbac.Generator{}, 53 "object": deepcopy.Generator{}, 54 "webhook": webhook.Generator{}, 55 "schemapatch": schemapatcher.Generator{}, 56 } 57 58 // allOutputRules defines the list of all known output rules, giving 59 // them names for use on the command line. 60 // Each output rule turns into two command line options: 61 // - output:<generator>:<form> (per-generator output) 62 // - output:<form> (default output) 63 allOutputRules = map[string]genall.OutputRule{ 64 "dir": genall.OutputToDirectory(""), 65 "none": genall.OutputToNothing, 66 "stdout": genall.OutputToStdout, 67 "artifacts": genall.OutputArtifacts{}, 68 } 69 70 // optionsRegistry contains all the marker definitions used to process command line options 71 optionsRegistry = &markers.Registry{} 72 ) 73 74 func init() { 75 for genName, gen := range allGenerators { 76 // make the generator options marker itself 77 defn := markers.Must(markers.MakeDefinition(genName, markers.DescribesPackage, gen)) 78 if err := optionsRegistry.Register(defn); err != nil { 79 panic(err) 80 } 81 if helpGiver, hasHelp := gen.(genall.HasHelp); hasHelp { 82 if help := helpGiver.Help(); help != nil { 83 optionsRegistry.AddHelp(defn, help) 84 } 85 } 86 87 // make per-generation output rule markers 88 for ruleName, rule := range allOutputRules { 89 ruleMarker := markers.Must(markers.MakeDefinition(fmt.Sprintf("output:%s:%s", genName, ruleName), markers.DescribesPackage, rule)) 90 if err := optionsRegistry.Register(ruleMarker); err != nil { 91 panic(err) 92 } 93 if helpGiver, hasHelp := rule.(genall.HasHelp); hasHelp { 94 if help := helpGiver.Help(); help != nil { 95 optionsRegistry.AddHelp(ruleMarker, help) 96 } 97 } 98 } 99 } 100 101 // make "default output" output rule markers 102 for ruleName, rule := range allOutputRules { 103 ruleMarker := markers.Must(markers.MakeDefinition("output:"+ruleName, markers.DescribesPackage, rule)) 104 if err := optionsRegistry.Register(ruleMarker); err != nil { 105 panic(err) 106 } 107 if helpGiver, hasHelp := rule.(genall.HasHelp); hasHelp { 108 if help := helpGiver.Help(); help != nil { 109 optionsRegistry.AddHelp(ruleMarker, help) 110 } 111 } 112 } 113 114 // add in the common options markers 115 if err := genall.RegisterOptionsMarkers(optionsRegistry); err != nil { 116 panic(err) 117 } 118 } 119 120 // noUsageError suppresses usage printing when it occurs 121 // (since cobra doesn't provide a good way to avoid printing 122 // out usage in only certain situations). 123 type noUsageError struct{ error } 124 125 func main() { 126 helpLevel := 0 127 whichLevel := 0 128 showVersion := false 129 130 cmd := &cobra.Command{ 131 Use: "controller-gen", 132 Short: "Generate Kubernetes API extension resources and code.", 133 Long: "Generate Kubernetes API extension resources and code.", 134 Example: ` # Generate RBAC manifests and crds for all types under apis/, 135 # outputting crds to /tmp/crds and everything else to stdout 136 controller-gen rbac:roleName=<role name> crd paths=./apis/... output:crd:dir=/tmp/crds output:stdout 137 138 # Generate deepcopy/runtime.Object implementations for a particular file 139 controller-gen object paths=./apis/v1beta1/some_types.go 140 141 # Generate OpenAPI v3 schemas for API packages and merge them into existing CRD manifests 142 controller-gen schemapatch:manifests=./manifests output:dir=./manifests paths=./pkg/apis/... 143 144 # Run all the generators for a given project 145 controller-gen paths=./apis/... 146 147 # Explain the markers for generating CRDs, and their arguments 148 controller-gen crd -ww 149 `, 150 RunE: func(c *cobra.Command, rawOpts []string) error { 151 // print version if asked for it 152 if showVersion { 153 version.Print() 154 return nil 155 } 156 157 // print the help if we asked for it (since we've got a different help flag :-/), then bail 158 if helpLevel > 0 { 159 return c.Usage() 160 } 161 162 // print the marker docs if we asked for them, then bail 163 if whichLevel > 0 { 164 return printMarkerDocs(c, rawOpts, whichLevel) 165 } 166 167 // otherwise, set up the runtime for actually running the generators 168 rt, err := genall.FromOptions(optionsRegistry, rawOpts) 169 if err != nil { 170 return err 171 } 172 if len(rt.Generators) == 0 { 173 return fmt.Errorf("no generators specified") 174 } 175 176 if hadErrs := rt.Run(); hadErrs { 177 // don't obscure the actual error with a bunch of usage 178 return noUsageError{fmt.Errorf("not all generators ran successfully")} 179 } 180 return nil 181 }, 182 SilenceUsage: true, // silence the usage, then print it out ourselves if it wasn't suppressed 183 } 184 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)") 185 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)") 186 cmd.Flags().BoolVar(&showVersion, "version", false, "show version") 187 cmd.Flags().Bool("help", false, "print out usage and a summary of options") 188 oldUsage := cmd.UsageFunc() 189 cmd.SetUsageFunc(func(c *cobra.Command) error { 190 if err := oldUsage(c); err != nil { 191 return err 192 } 193 if helpLevel == 0 { 194 helpLevel = summaryHelp 195 } 196 fmt.Fprintf(c.OutOrStderr(), "\n\nOptions\n\n") 197 return helpForLevels(c.OutOrStdout(), c.OutOrStderr(), helpLevel, optionsRegistry, help.SortByOption) 198 }) 199 200 if err := cmd.Execute(); err != nil { 201 if _, noUsage := err.(noUsageError); !noUsage { 202 // print the usage unless we suppressed it 203 if err := cmd.Usage(); err != nil { 204 panic(err) 205 } 206 } 207 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:], " ")) 208 os.Exit(1) 209 } 210 } 211 212 // printMarkerDocs prints out marker help for the given generators specified in 213 // the rawOptions, at the given level. 214 func printMarkerDocs(c *cobra.Command, rawOptions []string, whichLevel int) error { 215 // just grab a registry so we don't lag while trying to load roots 216 // (like we'd do if we just constructed the full runtime). 217 reg, err := genall.RegistryFromOptions(optionsRegistry, rawOptions) 218 if err != nil { 219 return err 220 } 221 222 return helpForLevels(c.OutOrStdout(), c.OutOrStderr(), whichLevel, reg, help.SortByCategory) 223 } 224 225 func helpForLevels(mainOut io.Writer, errOut io.Writer, whichLevel int, reg *markers.Registry, sorter help.SortGroup) error { 226 helpInfo := help.ByCategory(reg, sorter) 227 switch whichLevel { 228 case jsonHelp: 229 if err := json.NewEncoder(mainOut).Encode(helpInfo); err != nil { 230 return err 231 } 232 case detailedHelp, fullHelp: 233 fullDetail := whichLevel == fullHelp 234 for _, cat := range helpInfo { 235 if cat.Category == "" { 236 continue 237 } 238 contents := prettyhelp.MarkersDetails(fullDetail, cat.Category, cat.Markers) 239 if err := contents.WriteTo(errOut); err != nil { 240 return err 241 } 242 } 243 case summaryHelp: 244 for _, cat := range helpInfo { 245 if cat.Category == "" { 246 continue 247 } 248 contents := prettyhelp.MarkersSummary(cat.Category, cat.Markers) 249 if err := contents.WriteTo(errOut); err != nil { 250 return err 251 } 252 } 253 } 254 return nil 255 } 256 257 const ( 258 _ = iota 259 summaryHelp 260 detailedHelp 261 fullHelp 262 jsonHelp 263 )