main.go 8.7 KB

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