github.com/jenkins-x/jx-api@v0.0.24/cmd/codegen/app/generate_openapi.go (about)

     1  package app
     2  
     3  import (
     4  	"go/build"
     5  	"os"
     6  	"path/filepath"
     7  	"strings"
     8  
     9  	"github.com/jenkins-x/jx-logging/pkg/log"
    10  
    11  	"github.com/jenkins-x/jx-api/cmd/codegen/generator"
    12  	"github.com/jenkins-x/jx-api/cmd/codegen/util"
    13  
    14  	"github.com/pkg/errors"
    15  
    16  	"github.com/spf13/cobra"
    17  )
    18  
    19  // CreateClientOpenAPIOptions the options for the create client openapi command
    20  type CreateClientOpenAPIOptions struct {
    21  	GenerateOptions
    22  	Title                string
    23  	Version              string
    24  	ReferenceDocsVersion string
    25  	OpenAPIDependencies  []string
    26  	OpenAPIOutputDir     string
    27  	ModuleName           string
    28  }
    29  
    30  var (
    31  	createClientOpenAPILong = `This command code generates OpenAPI specs for
    32  the specified custom resources.
    33  `
    34  
    35  	createClientOpenAPIExample = `
    36  # lets generate client docs
    37  codegen openapi
    38  	--output-package=github.com/jenkins-x/jx-api/pkg/client \
    39  	--input-package=github.com/jenkins-x/jx-api/pkg-apis \
    40  	--group-with-version=jenkins.io:v1
    41  	--version=1.2.3
    42  	--title=Jenkins X
    43  
    44  # You will normally want to add a target to your Makefile that looks like
    45  generate-openapi:
    46  	codegen openapi
    47  		--output-package=github.com/jenkins-x/jx-api/pkg/client \
    48  		--input-package=github.com/jenkins-x/jx-api/pkg/apis \
    49  		--group-with-version=jenkins.io:v1
    50  		--version=${VERSION}
    51  		--title=${TITLE}
    52  
    53  # and then call
    54  make generate-openapi
    55  `
    56  )
    57  
    58  // NewCmdCreateClientOpenAPI creates the command
    59  func NewCmdCreateClientOpenAPI(genOpts GenerateOptions) *cobra.Command {
    60  	o := &CreateClientOpenAPIOptions{
    61  		GenerateOptions: genOpts,
    62  	}
    63  
    64  	cobraCmd := &cobra.Command{
    65  		Use:     "openapi",
    66  		Short:   "Creates OpenAPI specs for Custom Resources",
    67  		Long:    createClientOpenAPILong,
    68  		Example: createClientOpenAPIExample,
    69  
    70  		Run: func(c *cobra.Command, args []string) {
    71  			o.Cmd = c
    72  			o.Args = args
    73  			err := o.Run()
    74  			util.CheckErr(err)
    75  		},
    76  	}
    77  
    78  	wd, err := os.Getwd()
    79  	if err != nil {
    80  		log.Logger().Warnf("Error getting working directory for %v\n", err)
    81  	}
    82  
    83  	openAPIDependencies := []string{
    84  		"k8s.io/apimachinery?modules:pkg/apis:meta:v1",
    85  		"k8s.io/apimachinery?modules:pkg/api:resource:",
    86  		"k8s.io/apimachinery?modules:pkg/util:intstr:",
    87  		"k8s.io/api?modules::batch:v1",
    88  		"k8s.io/api?modules::core:v1",
    89  		"k8s.io/api?modules::rbac:v1",
    90  	}
    91  
    92  	moduleName := strings.TrimPrefix(strings.TrimPrefix(wd, filepath.Join(build.Default.GOPATH, "src")), "/")
    93  
    94  	cobraCmd.Flags().StringVarP(&o.OutputBase, "output-base", "", wd,
    95  		"Output base directory, by default the current working directory")
    96  	cobraCmd.Flags().StringVarP(&o.BoilerplateFile, optionBoilerplateFile, "", "custom-boilerplate.go.txt",
    97  		"Custom boilerplate to add to all files if the file is missing it will be ignored")
    98  	cobraCmd.Flags().StringVarP(&o.InputBase, optionInputBase, "", wd,
    99  		"Input base (the root of module the OpenAPI is being generated for), by default the current working directory")
   100  	cobraCmd.Flags().StringVarP(&o.InputPackage, optionInputPackage, "i", "", "Input package (relative to input base), "+
   101  		"must specify")
   102  	cobraCmd.Flags().StringVarP(&o.OutputPackage, optionOutputPackage, "o", "", "Output package, must specify")
   103  	cobraCmd.Flags().StringVarP(&o.Title, "title", "", "Jenkins X", "Title for OpenAPI, JSON Schema and HTML docs")
   104  	cobraCmd.Flags().StringVarP(&o.Version, "version", "", "", "Version for OpenAPI, JSON Schema and HTML docs")
   105  	cobraCmd.Flags().StringArrayVarP(&o.OpenAPIDependencies, "open-api-dependency", "", openAPIDependencies,
   106  		"Add <path?modules:package:group:apiVersion> dependencies for OpenAPI generation")
   107  	cobraCmd.Flags().StringVarP(&o.OpenAPIOutputDir, "openapi-output-directory", "",
   108  		"docs/apidocs", "Output directory for the OpenAPI specs, "+
   109  			"relative to the output-base unless absolute. "+
   110  			"OpenAPI spec JSON and YAML files are placed in openapi-spec sub directory.")
   111  	cobraCmd.Flags().StringArrayVarP(&o.GroupsWithVersions, optionGroupWithVersion, "g", make([]string, 0),
   112  		"group name:version (e.g. jenkins.io:v1) to generate, must specify at least once")
   113  	cobraCmd.Flags().StringVarP(&o.ModuleName, optionModuleName, "", moduleName,
   114  		"module name (e.g. github.com/jenkins-x/jx)")
   115  	cobraCmd.Flags().BoolVarP(&o.Global, global, "", false, "use the users GOPATH")
   116  	cobraCmd.Flags().StringVarP(&o.SemVer, optionSemVer, "", "", "semantic version to use in packages")
   117  	return cobraCmd
   118  }
   119  
   120  // Run implements this command
   121  func (o *CreateClientOpenAPIOptions) Run() error {
   122  	var err error
   123  	o.BoilerplateFile, err = generator.GetBoilerplateFile(o.BoilerplateFile)
   124  	if err != nil {
   125  		return errors.Wrapf(err, "reading file %s specified by %s", o.BoilerplateFile, optionBoilerplateFile)
   126  	}
   127  	if o.InputPackage == "" {
   128  		return util.MissingOption(optionInputPackage)
   129  	}
   130  	if o.OutputPackage == "" {
   131  		return util.MissingOption(optionOutputPackage)
   132  	}
   133  
   134  	err = o.configure()
   135  	if err != nil {
   136  		return errors.Wrapf(err, "ensuring GOPATH is set correctly")
   137  	}
   138  
   139  	if len(o.GroupsWithVersions) < 1 {
   140  		return util.InvalidOptionf(optionGroupWithVersion, o.GroupsWithVersions, "must specify at least once")
   141  	}
   142  
   143  	cleanupFunc := func() {}
   144  
   145  	gopath := util.GoPath()
   146  	if !o.Global {
   147  		gopath, err = util.IsolatedGoPath()
   148  		if err != nil {
   149  			return errors.Wrapf(err, "getting isolated gopath")
   150  		}
   151  		cleanupFunc, err = util.BackupGoModAndGoSum()
   152  		if err != nil {
   153  			return errors.Wrapf(err, "backing up go.mod and go.sum")
   154  		}
   155  	}
   156  
   157  	defer cleanupFunc()
   158  
   159  	err = generator.InstallOpenApiGen(o.GeneratorVersion, gopath)
   160  	if err != nil {
   161  		return errors.Wrapf(err, "error installing kubernetes openapi tools")
   162  	}
   163  
   164  	if !filepath.IsAbs(o.OpenAPIOutputDir) {
   165  		o.OpenAPIOutputDir = filepath.Join(o.OutputBase, o.OpenAPIOutputDir)
   166  	}
   167  
   168  	log.Logger().Infof("generating Go code to %s in package %s from package %s\n", o.OutputBase, o.GoPathOutputPackage, o.InputPackage)
   169  	err = generator.GenerateOpenApi(o.GroupsWithVersions, o.InputPackage, o.GoPathOutputPackage, o.OutputPackage,
   170  		filepath.Join(build.Default.GOPATH, "src"), o.OpenAPIDependencies, o.InputBase, o.ModuleName, o.BoilerplateFile, gopath, o.SemVer)
   171  	if err != nil {
   172  		return errors.Wrapf(err, "generating openapi structs to %s", o.GoPathOutputPackage)
   173  	}
   174  
   175  	log.Logger().Infof("generating OpenAPI spec files to %s from package %s\n", o.OpenAPIOutputDir, filepath.Join(o.InputBase,
   176  		o.InputPackage))
   177  	err = generator.GenerateSchema(o.OpenAPIOutputDir, o.OutputPackage, o.InputBase, o.Title, o.Version, gopath)
   178  	if err != nil {
   179  		return errors.Wrapf(err, "generating schema to %s", o.OpenAPIOutputDir)
   180  	}
   181  	return nil
   182  }