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 }