github.skymusic.top/operator-framework/operator-sdk@v0.8.2/cmd/operator-sdk/internal/genutil/openapi.go (about) 1 // Copyright 2018 The Operator-SDK Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package genutil 16 17 import ( 18 "fmt" 19 "os/exec" 20 "path/filepath" 21 "strings" 22 23 "github.com/operator-framework/operator-sdk/internal/pkg/scaffold" 24 "github.com/operator-framework/operator-sdk/internal/pkg/scaffold/input" 25 "github.com/operator-framework/operator-sdk/internal/util/k8sutil" 26 "github.com/operator-framework/operator-sdk/internal/util/projutil" 27 28 log "github.com/sirupsen/logrus" 29 ) 30 31 // OpenAPIGen generates OpenAPI validation specs for all CRD's in dirs. 32 func OpenAPIGen() error { 33 projutil.MustInProjectRoot() 34 35 absProjectPath := projutil.MustGetwd() 36 repoPkg := projutil.CheckAndGetProjectGoPkg() 37 srcDir := filepath.Join(absProjectPath, "vendor", "k8s.io", "kube-openapi") 38 binDir := filepath.Join(absProjectPath, scaffold.BuildBinDir) 39 40 if err := buildOpenAPIGenBinary(binDir, srcDir); err != nil { 41 return err 42 } 43 44 gvMap, err := parseGroupVersions() 45 if err != nil { 46 return fmt.Errorf("failed to parse group versions: (%v)", err) 47 } 48 gvb := &strings.Builder{} 49 for g, vs := range gvMap { 50 gvb.WriteString(fmt.Sprintf("%s:%v, ", g, vs)) 51 } 52 53 log.Infof("Running OpenAPI code-generation for Custom Resource group versions: [%v]\n", gvb.String()) 54 55 apisPkg := filepath.Join(repoPkg, scaffold.ApisDir) 56 fqApiStr := createFQApis(apisPkg, gvMap) 57 fqApis := strings.Split(fqApiStr, ",") 58 f := func(a string) error { return openAPIGen(binDir, a, fqApis) } 59 if err = withHeaderFile(f); err != nil { 60 return err 61 } 62 63 s := &scaffold.Scaffold{} 64 cfg := &input.Config{ 65 Repo: repoPkg, 66 AbsProjectPath: absProjectPath, 67 ProjectName: filepath.Base(absProjectPath), 68 } 69 crds, err := k8sutil.GetCRDs(scaffold.CRDsDir) 70 if err != nil { 71 return err 72 } 73 for _, crd := range crds { 74 g, v, k := crd.Spec.Group, crd.Spec.Version, crd.Spec.Names.Kind 75 if v == "" { 76 if len(crd.Spec.Versions) != 0 { 77 v = crd.Spec.Versions[0].Name 78 } else { 79 return fmt.Errorf("crd of group %s kind %s has no version", g, k) 80 } 81 } 82 r, err := scaffold.NewResource(g+"/"+v, k) 83 if err != nil { 84 return err 85 } 86 err = s.Execute(cfg, 87 &scaffold.CRD{Resource: r, IsOperatorGo: projutil.IsOperatorGo()}, 88 ) 89 if err != nil { 90 return err 91 } 92 } 93 94 log.Info("Code-generation complete.") 95 return nil 96 } 97 98 func buildOpenAPIGenBinary(binDir, codegenSrcDir string) error { 99 genDirs := []string{"./cmd/openapi-gen"} 100 return buildCodegenBinaries(genDirs, binDir, codegenSrcDir) 101 } 102 103 func openAPIGen(binDir, hf string, fqApis []string) (err error) { 104 cgPath := filepath.Join(binDir, "openapi-gen") 105 for _, fqApi := range fqApis { 106 args := []string{ 107 "--input-dirs", fqApi, 108 "--output-package", fqApi, 109 "--output-file-base", "zz_generated.openapi", 110 // openapi-gen requires a boilerplate file. Either use header or an 111 // empty file if header is empty. 112 "--go-header-file", hf, 113 } 114 cmd := exec.Command(cgPath, args...) 115 if err = projutil.ExecCmd(cmd); err != nil { 116 return fmt.Errorf("failed to perform openapi code-generation: %v", err) 117 } 118 } 119 return nil 120 }