github.com/waynz0r/controller-tools@v0.4.1-0.20200916220028-16254aeef2d7/pkg/genall/genall.go (about) 1 /* 2 Copyright 2019 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 17 package genall 18 19 import ( 20 "fmt" 21 "io" 22 "io/ioutil" 23 "os" 24 25 "golang.org/x/tools/go/packages" 26 "sigs.k8s.io/yaml" 27 28 "sigs.k8s.io/controller-tools/pkg/loader" 29 "sigs.k8s.io/controller-tools/pkg/markers" 30 ) 31 32 // Generators are a list of Generators. 33 // NB(directxman12): this is a pointer so that we can uniquely identify each 34 // instance of a generator, even if it's not hashable. Different *instances* 35 // of a generator are treated differently. 36 type Generators []*Generator 37 38 // RegisterMarkers registers all markers defined by each of the Generators in 39 // this list into the given registry. 40 func (g Generators) RegisterMarkers(reg *markers.Registry) error { 41 for _, gen := range g { 42 if err := (*gen).RegisterMarkers(reg); err != nil { 43 return err 44 } 45 } 46 return nil 47 } 48 49 // Generator knows how to register some set of markers, and then produce 50 // output artifacts based on loaded code containing those markers, 51 // sharing common loaded data. 52 type Generator interface { 53 // RegisterMarkers registers all markers needed by this Generator 54 // into the given registry. 55 RegisterMarkers(into *markers.Registry) error 56 // Generate generates artifacts produced by this marker. 57 // It's called *after* RegisterMarkers has been called. 58 Generate(*GenerationContext) error 59 } 60 61 // HasHelp is some Generator, OutputRule, etc with a help method. 62 type HasHelp interface { 63 // Help returns help for this generator. 64 Help() *markers.DefinitionHelp 65 } 66 67 // Runtime collects generators, loaded program data (Collector, root Packages), 68 // and I/O rules, running them together. 69 type Runtime struct { 70 // Generators are the Generators to be run by this Runtime. 71 Generators Generators 72 // GenerationContext is the base generation context that's copied 73 // to produce the context for each Generator. 74 GenerationContext 75 // OutputRules defines how to output artifacts for each Generator. 76 OutputRules OutputRules 77 } 78 79 // GenerationContext defines the common information needed for each Generator 80 // to run. 81 type GenerationContext struct { 82 // Collector is the shared marker collector. 83 Collector *markers.Collector 84 // Roots are the base packages to be processed. 85 Roots []*loader.Package 86 // Checker is the shared partial type-checker. 87 Checker *loader.TypeChecker 88 // OutputRule describes how to output artifacts. 89 OutputRule 90 // InputRule describes how to load associated boilerplate artifacts. 91 // It should *not* be used to load source files. 92 InputRule 93 } 94 95 // WriteYAML writes the given objects out, serialized as YAML, using the 96 // context's OutputRule. Objects are written as separate documents, separated 97 // from each other by `---` (as per the YAML spec). 98 func (g GenerationContext) WriteYAML(itemPath string, objs ...interface{}) error { 99 out, err := g.Open(nil, itemPath) 100 if err != nil { 101 return err 102 } 103 defer out.Close() 104 105 for _, obj := range objs { 106 yamlContent, err := yaml.Marshal(obj) 107 if err != nil { 108 return err 109 } 110 n, err := out.Write(append([]byte("\n---\n"), yamlContent...)) 111 if err != nil { 112 return err 113 } 114 if n < len(yamlContent) { 115 return io.ErrShortWrite 116 } 117 } 118 119 return nil 120 } 121 122 // ReadFile reads the given boilerplate artifact using the context's InputRule. 123 func (g GenerationContext) ReadFile(path string) ([]byte, error) { 124 file, err := g.OpenForRead(path) 125 if err != nil { 126 return nil, err 127 } 128 defer file.Close() 129 return ioutil.ReadAll(file) 130 } 131 132 // ForRoots produces a Runtime to run the given generators against the 133 // given packages. It outputs to /dev/null by default. 134 func (g Generators) ForRoots(rootPaths ...string) (*Runtime, error) { 135 roots, err := loader.LoadRoots(rootPaths...) 136 if err != nil { 137 return nil, err 138 } 139 rt := &Runtime{ 140 Generators: g, 141 GenerationContext: GenerationContext{ 142 Collector: &markers.Collector{ 143 Registry: &markers.Registry{}, 144 }, 145 Roots: roots, 146 InputRule: InputFromFileSystem, 147 Checker: &loader.TypeChecker{}, 148 }, 149 OutputRules: OutputRules{Default: OutputToNothing}, 150 } 151 if err := rt.Generators.RegisterMarkers(rt.Collector.Registry); err != nil { 152 return nil, err 153 } 154 return rt, nil 155 } 156 157 // Run runs the Generators in this Runtime against its packages, printing 158 // errors (except type errors, which common result from using TypeChecker with 159 // filters), returning true if errors were found. 160 func (r *Runtime) Run() bool { 161 // TODO(directxman12): we could make this parallel, 162 // but we'd need to ensure all underlying machinery is threadsafe 163 if len(r.Generators) == 0 { 164 fmt.Fprintln(os.Stderr, "no generators to run") 165 return true 166 } 167 168 for _, gen := range r.Generators { 169 ctx := r.GenerationContext // make a shallow copy 170 ctx.OutputRule = r.OutputRules.ForGenerator(gen) 171 if err := (*gen).Generate(&ctx); err != nil { 172 fmt.Fprintln(os.Stderr, err) 173 } 174 } 175 176 // skip TypeErrors -- they're probably just from partial typechecking in crd-gen 177 return loader.PrintErrors(r.Roots, packages.TypeError) 178 }