github.com/jenkins-x/jx/v2@v2.1.155/cmd/codegen/generator/client_set.go (about)

     1  package generator
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"go/ast"
     7  	"go/format"
     8  	"go/parser"
     9  	"go/token"
    10  	"io/ioutil"
    11  	"os"
    12  	"path/filepath"
    13  	"strings"
    14  
    15  	"github.com/jenkins-x/jx/v2/cmd/codegen/util"
    16  	"github.com/pkg/errors"
    17  	"golang.org/x/tools/go/ast/astutil"
    18  )
    19  
    20  const (
    21  	basePath       = "k8s.io/code-generator/cmd"
    22  	defaultVersion = "kubernetes-1.11.3"
    23  )
    24  
    25  var (
    26  	// AllGenerators is a list of all the generators provide by kubernetes code-generator
    27  	allGenerators = map[string]string{
    28  		"clientset": "client-gen",
    29  		"defaulter": "defaulter-gen",
    30  		"listers":   "lister-gen",
    31  		"informers": "informer-gen",
    32  		"deepcopy":  "deepcopy-gen",
    33  	}
    34  )
    35  
    36  // InstallCodeGenerators installs client-gen from the kubernetes-incubator/reference-docs repository.
    37  func InstallCodeGenerators(version string, gopath string) error {
    38  	if version == "" {
    39  		version = defaultVersion
    40  	}
    41  	path := fmt.Sprintf("%s/...", basePath)
    42  	util.AppLogger().Infof("installing %s version %s into %s", path, version, gopath)
    43  	err := util.GoGet(path, version, gopath, true, false, false)
    44  	if err != nil {
    45  		return err
    46  	}
    47  
    48  	return nil
    49  }
    50  
    51  // GenerateClient runs the generators specified. It looks at the specified groupsWithVersions in inputPackage and generates to outputPackage (
    52  //// relative to the module outputBase). A boilerplateFile is written to the top of any generated files.
    53  func GenerateClient(generators []string, groupsWithVersions []string, inputPackage string, outputPackage string,
    54  	relativePackage string, outputBase string, boilerplateFile string, gopath string, semVer string) error {
    55  	for _, generatorName := range generators {
    56  		if generator, ok := allGenerators[generatorName]; ok {
    57  			switch generatorName {
    58  			case "clientset":
    59  				err := generateWithOutputPackage(generator,
    60  					generatorName,
    61  					groupsWithVersions,
    62  					inputPackage,
    63  					outputPackage,
    64  					outputBase,
    65  					boilerplateFile,
    66  					gopath,
    67  					"--clientset-name",
    68  					"versioned")
    69  				if err != nil {
    70  					return err
    71  				}
    72  				if semVer != "" {
    73  					oldPkg := filepath.Join(outputPackage, generatorName)
    74  					csDir := filepath.Join(outputBase, oldPkg)
    75  					svPkg := strings.ReplaceAll(oldPkg, fmt.Sprintf("/%s", relativePackage), fmt.Sprintf("/%s/%s", semVer, relativePackage))
    76  					err = fixClientImportsForSemVer(csDir, oldPkg, svPkg)
    77  					if err != nil {
    78  						return errors.Wrapf(err, "updating clientset imports for semver")
    79  					}
    80  				}
    81  			case "deepcopy":
    82  				err := defaultGenerate(generator,
    83  					generatorName,
    84  					groupsWithVersions,
    85  					inputPackage,
    86  					outputPackage,
    87  					outputBase,
    88  					boilerplateFile,
    89  					gopath,
    90  					"-O",
    91  					"zz_generated.deepcopy",
    92  					"--bounding-dirs",
    93  					inputPackage)
    94  				if err != nil {
    95  					return err
    96  				}
    97  				// If we have a semver, copy the semver's pkg directory contents to the normal pkg and delete the semver directory
    98  				if semVer != "" {
    99  					wd, err := os.Getwd()
   100  					if err != nil {
   101  						return errors.Wrapf(err, "getting working directory")
   102  					}
   103  					pkgs, err := packagesForGroupsWithVersions(inputPackage, groupsWithVersions)
   104  					if err != nil {
   105  						return err
   106  					}
   107  					for _, p := range pkgs {
   108  						svFile := filepath.Join(outputBase, inputPackage, p, "zz_generated.deepcopy.go")
   109  						pkgFile := strings.ReplaceAll(svFile, fmt.Sprintf("/%s/", semVer), "/")
   110  						exists, err := util.FileExists(svFile)
   111  						if err != nil {
   112  							return errors.Wrapf(err, "checking if file %s exists", svFile)
   113  						}
   114  						if exists {
   115  							err = util.CopyFile(svFile, pkgFile)
   116  							if err != nil {
   117  								return errors.Wrapf(err, "copying %s to %s", svFile, pkgFile)
   118  							}
   119  						}
   120  					}
   121  					err = os.RemoveAll(filepath.Join(wd, semVer))
   122  				}
   123  			case "informers":
   124  				basePkg := outputPackage
   125  				if semVer != "" {
   126  					basePkg = strings.ReplaceAll(basePkg, fmt.Sprintf("/%s", relativePackage), fmt.Sprintf("/%s/%s", semVer, relativePackage))
   127  				}
   128  				err := generateWithOutputPackage(generator,
   129  					generatorName,
   130  					groupsWithVersions,
   131  					inputPackage,
   132  					outputPackage,
   133  					outputBase,
   134  					boilerplateFile,
   135  					gopath,
   136  					"--versioned-clientset-package",
   137  					fmt.Sprintf("%s/clientset/versioned", basePkg),
   138  					"--listers-package",
   139  					fmt.Sprintf("%s/listers", basePkg))
   140  				if err != nil {
   141  					return err
   142  				}
   143  				if semVer != "" {
   144  					oldPkg := filepath.Join(outputPackage, generatorName)
   145  					infDir := filepath.Join(outputBase, oldPkg)
   146  					svPkg := strings.ReplaceAll(oldPkg, fmt.Sprintf("/%s", relativePackage), fmt.Sprintf("/%s/%s", semVer, relativePackage))
   147  					err = fixClientImportsForSemVer(infDir, oldPkg, svPkg)
   148  					if err != nil {
   149  						return errors.Wrapf(err, "updating informer imports for semver")
   150  					}
   151  				}
   152  			default:
   153  				err := generateWithOutputPackage(generator, generatorName, groupsWithVersions, inputPackage,
   154  					outputPackage, outputBase, boilerplateFile, gopath)
   155  				if err != nil {
   156  					return err
   157  				}
   158  			}
   159  		} else {
   160  			return errors.Errorf("no generator %s available", generatorName)
   161  		}
   162  	}
   163  	return nil
   164  }
   165  
   166  // GetBoilerplateFile is responsible for canonicalizing the name of the boilerplate file
   167  func GetBoilerplateFile(fileName string) (string, error) {
   168  	if fileName != "" {
   169  		if _, err := os.Stat(fileName); err != nil && !os.IsNotExist(err) {
   170  			return "", errors.Wrapf(err, "error reading boilerplate file %s", fileName)
   171  		} else if err == nil {
   172  			abs, err := filepath.Abs(fileName)
   173  			if err == nil {
   174  				fileName = abs
   175  			} else {
   176  				util.AppLogger().Errorf("error converting %s to absolute path so leaving as is %v", fileName, err)
   177  			}
   178  		}
   179  	}
   180  	return fileName, nil
   181  }
   182  
   183  func generateWithOutputPackage(generator string, name string, groupsWithVersions []string,
   184  	inputPackage string, outputPackage string, outputBase string, boilerplateFile string, gopath string, args ...string) error {
   185  	args = append(args, "--output-package", fmt.Sprintf("%s/%s", outputPackage, name))
   186  	return defaultGenerate(generator, name, groupsWithVersions, inputPackage, outputPackage, outputBase,
   187  		boilerplateFile, gopath, args...)
   188  }
   189  
   190  func fixClientImportsForSemVer(clientDir string, oldPackage string, semVerPackage string) error {
   191  	return filepath.Walk(clientDir, func(path string, info os.FileInfo, err error) error {
   192  		if strings.HasSuffix(path, ".go") {
   193  			fs := token.NewFileSet()
   194  			f, err := parser.ParseFile(fs, path, nil, parser.ParseComments)
   195  			if err != nil {
   196  				return errors.Wrapf(err, "parsing %s", path)
   197  			}
   198  			importsToReplace := make(map[string]string)
   199  
   200  			for _, imp := range f.Imports {
   201  				existingImp := strings.Replace(imp.Path.Value, `"`, ``, 2)
   202  				if strings.HasPrefix(existingImp, oldPackage) {
   203  					importsToReplace[existingImp] = strings.ReplaceAll(existingImp, oldPackage, semVerPackage)
   204  				}
   205  			}
   206  			if len(importsToReplace) > 0 {
   207  				for oldPkg, newPkg := range importsToReplace {
   208  					astutil.RewriteImport(fs, f, oldPkg, newPkg)
   209  				}
   210  				// Sort the imports
   211  				ast.SortImports(fs, f)
   212  				var buf bytes.Buffer
   213  				err = format.Node(&buf, fs, f)
   214  				if err != nil {
   215  					return errors.Wrapf(err, "convert AST to []byte for %s", path)
   216  				}
   217  				err = ioutil.WriteFile(path, buf.Bytes(), 0600)
   218  				if err != nil {
   219  					return errors.Wrapf(err, "writing %s", path)
   220  				}
   221  			}
   222  		}
   223  		return nil
   224  	})
   225  }