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  }