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