diff --git a/CHANGELOG.md b/CHANGELOG.md index a013041e266..41c362d85d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Added - Support for vars in top level ansible watches. ([#2147](https://github.com/operator-framework/operator-sdk/pull/2147)) - Support for `"ansible.operator-sdk/verbosity"` annotation on Custom Resources watched by Ansible based operators to override verbosity on an individual resource. ([#2102](https://github.com/operator-framework/operator-sdk/pull/2102)) +- New `operator-sdk generate crds` subcommand, which generates CRDs from Go types. ([#2276](https://github.com/operator-framework/operator-sdk/pull/2276)) ### Changed - Upgrade minimal Ansible version in the init projects from `2.4` to `2.6`. ([#2107](https://github.com/operator-framework/operator-sdk/pull/2107)) @@ -13,12 +14,9 @@ - Replace in the Ansible based operators module tests `k8s_info` for `k8s_facts` which is deprecated. ([#2168](https://github.com/operator-framework/operator-sdk/issues/2168)) - Upgrade the Ansible version from `2.8` to `2.9` on the Ansible based operators image. ([#2168](https://github.com/operator-framework/operator-sdk/issues/2168)) - Updated CRD generation for non-Go operators to use valid structural schema. ([#2275](https://github.com/operator-framework/operator-sdk/issues/2275)) -- **Breaking change:** Renamed `operator-sdk generate openapi` to `operator-sdk generate crds`. ([#2276](https://github.com/operator-framework/operator-sdk/pull/2276)) ### Deprecated - -### Removed -- Removed generation of `zz_generated.openapi.go` files during `operator-sdk generate openapi`. ([#2276](https://github.com/operator-framework/operator-sdk/pull/2276)) +- Deprecated the `operator-sdk generate openapi` command. CRD generation is still supported with `operator-sdk generate crds`. It is now recommended to use [openapi-gen](https://github.com/kubernetes/kube-openapi/tree/master/cmd/openapi-gen) directly for OpenAPI code generation. The `generate openapi` subcommand will be removed in a future release. ([#2276](https://github.com/operator-framework/operator-sdk/pull/2276)) ### Bug Fixes - Fix issue faced in the Ansible based operators when `jmespath` queries are used because it was not installed. ([#2252](https://github.com/operator-framework/operator-sdk/pull/2252)) diff --git a/cmd/operator-sdk/generate/cmd.go b/cmd/operator-sdk/generate/cmd.go index 71ce63b1b2f..b8bea95e55c 100644 --- a/cmd/operator-sdk/generate/cmd.go +++ b/cmd/operator-sdk/generate/cmd.go @@ -26,5 +26,6 @@ func NewCmd() *cobra.Command { } cmd.AddCommand(newGenerateK8SCmd()) cmd.AddCommand(newGenerateCRDsCmd()) + cmd.AddCommand(newGenerateOpenAPICmd()) return cmd } diff --git a/cmd/operator-sdk/generate/openapi.go b/cmd/operator-sdk/generate/openapi.go new file mode 100644 index 00000000000..44fab37ef04 --- /dev/null +++ b/cmd/operator-sdk/generate/openapi.go @@ -0,0 +1,82 @@ +// Copyright 2019 The Operator-SDK Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generate + +import ( + "fmt" + + "github.com/operator-framework/operator-sdk/cmd/operator-sdk/internal/genutil" + "github.com/spf13/cobra" +) + +func newGenerateOpenAPICmd() *cobra.Command { + return &cobra.Command{ + Hidden: true, + Use: "openapi", + Short: "Generates OpenAPI specs for API's", + Long: `generate openapi generates OpenAPI validation specs in Go from tagged types +in all pkg/apis// directories. Go code is generated under +pkg/apis///zz_generated.openapi.go. CRD's are generated, or +updated if they exist for a particular group + version + kind, under +deploy/crds/__crd.yaml; OpenAPI V3 validation YAML +is generated as a 'validation' object. + +Example: + + $ operator-sdk generate openapi + $ tree pkg/apis + pkg/apis/ + └── app + └── v1alpha1 + ├── zz_generated.openapi.go + $ tree deploy/crds + ├── deploy/crds/app.example.com_v1alpha1_appservice_cr.yaml + ├── deploy/crds/app.example.com_appservices_crd.yaml +`, + RunE: openAPIFunc, + } +} + +const deprecationTemplate = "\033[1;36m%s\033[0m" + +func openAPIFunc(cmd *cobra.Command, args []string) error { + fmt.Printf(deprecationTemplate, `[Deprecation notice] The 'operator-sdk generate openapi' command is deprecated! + + - To generate CRDs, use 'operator-sdk generate crds'. + - To generate Go OpenAPI code, use 'openapi-gen'. For example: + + # Build the latest openapi-gen from source + which ./bin/openapi-gen > /dev/null || go build -o ./bin/openapi-gen k8s.io/kube-openapi/cmd/openapi-gen + + # Run openapi-gen for each of your API group/version packages + ./bin/openapi-gen --logtostderr=true -o "" -i ./pkg/apis// -O zz_generated.openapi -p ./pkg/apis// -h ./hack/boilerplate.go.txt -r "-" + +`) + + if len(args) != 0 { + return fmt.Errorf("command %s doesn't accept any arguments", cmd.CommandPath()) + } + + fs := []func() error{ + genutil.OpenAPIGen, + genutil.CRDGen, + } + for _, f := range fs { + if err := f(); err != nil { + return err + } + } + return nil +} diff --git a/cmd/operator-sdk/internal/genutil/openapi.go b/cmd/operator-sdk/internal/genutil/openapi.go new file mode 100644 index 00000000000..57006a9e890 --- /dev/null +++ b/cmd/operator-sdk/internal/genutil/openapi.go @@ -0,0 +1,99 @@ +// Copyright 2018 The Operator-SDK Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package genutil + +import ( + "flag" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/operator-framework/operator-sdk/internal/scaffold" + "github.com/operator-framework/operator-sdk/internal/util/k8sutil" + "github.com/operator-framework/operator-sdk/internal/util/projutil" + + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" + generatorargs "k8s.io/kube-openapi/cmd/openapi-gen/args" + "k8s.io/kube-openapi/pkg/generators" +) + +// OpenAPIGen generates OpenAPI validation specs for all CRD's in dirs. +func OpenAPIGen() error { + projutil.MustInProjectRoot() + + repoPkg := projutil.GetGoPkg() + + gvMap, err := k8sutil.ParseGroupSubpackages(scaffold.ApisDir) + if err != nil { + return fmt.Errorf("failed to parse group versions: (%v)", err) + } + gvb := &strings.Builder{} + for g, vs := range gvMap { + gvb.WriteString(fmt.Sprintf("%s:%v, ", g, vs)) + } + + log.Infof("Running OpenAPI code-generation for Custom Resource group versions: [%v]\n", gvb.String()) + + apisPkg := filepath.Join(repoPkg, scaffold.ApisDir) + fqApis := k8sutil.CreateFQAPIs(apisPkg, gvMap) + f := func(a string) error { return openAPIGen(a, fqApis) } + if err = generateWithHeaderFile(f); err != nil { + return err + } + + log.Info("Code-generation complete.") + return nil +} + +func openAPIGen(hf string, fqApis []string) error { + wd, err := os.Getwd() + if err != nil { + return err + } + if err := flag.Set("logtostderr", "true"); err != nil { + return err + } + for _, api := range fqApis { + api = filepath.FromSlash(api) + // Use relative API path so the generator writes to the correct path. + apiPath := "." + string(filepath.Separator) + api[strings.Index(api, scaffold.ApisDir):] + args, cargs := generatorargs.NewDefaults() + // Ignore default output base and set our own output path. + args.OutputBase = "" + // openapi-gen already generates a "do not edit" comment. + args.GeneratedByCommentTemplate = "" + args.InputDirs = []string{apiPath} + args.OutputFileBaseName = "zz_generated.openapi" + args.OutputPackagePath = filepath.Join(wd, apiPath) + args.GoHeaderFilePath = hf + // Print API rule violations to stdout + cargs.ReportFilename = "-" + if err := generatorargs.Validate(args); err != nil { + return errors.Wrap(err, "openapi-gen argument validation error") + } + + err := args.Execute( + generators.NameSystems(), + generators.DefaultNameSystem(), + generators.Packages, + ) + if err != nil { + return errors.Wrap(err, "openapi-gen generator error") + } + } + return nil +} diff --git a/doc/sdk-cli-reference.md b/doc/sdk-cli-reference.md index f853407f0da..3bdca9aa648 100644 --- a/doc/sdk-cli-reference.md +++ b/doc/sdk-cli-reference.md @@ -132,6 +132,49 @@ pkg/apis/app/v1alpha1/ └── zz_generated.deepcopy.go ``` +### openapi + +> **DEPRECATED** +> +> The `operator-sdk generate openapi` command is deprecated +> - To generate CRDs, use 'operator-sdk generate crds'. +> - To generate Go OpenAPI code, use 'openapi-gen'. For example: +> +> ```console +> # Build the latest openapi-gen from source +> which ./bin/openapi-gen > /dev/null || go build -o ./bin/openapi-gen k8s.io/kube-openapi/cmd/openapi-gen +> +> # Run openapi-gen for each of your API group/version packages +> ./bin/openapi-gen --logtostderr=true -o "" -i ./pkg/apis// -O zz_generated.openapi -p ./pkg/apis// -h ./hack/boilerplate.go.txt -r "-" +> ``` + + +Runs the [kube-openapi][openapi-code-generator] OpenAPIv3 code generator for all Custom Resource Definition (CRD) API tagged fields under `pkg/apis/...`. + +**Note**: This command must be run every time a tagged API struct or struct field for a custom resource type is updated. + +#### Example + +```console +$ tree pkg/apis/app/v1alpha1/ +pkg/apis/app/v1alpha1/ +├── appservice_types.go +├── doc.go +└── register.go + +$ operator-sdk generate openapi +INFO[0000] Running OpenAPI code-generation for Custom Resource group versions: [app:[v1alpha1], ] +INFO[0001] Created deploy/crds/app.example.com_appservices_crd.yaml +INFO[0001] Code-generation complete. + +$ tree pkg/apis/app/v1alpha1/ +pkg/apis/app/v1alpha1/ +├── appservice_types.go +├── doc.go +├── register.go +└── zz_generated.openapi.go +``` + ### crds Runs CRD generation (based on controller-tools) for APIs under `pkg/apis/...`. @@ -145,7 +188,7 @@ $ tree pkg/apis/app/v1alpha1/ pkg/apis/app/v1alpha1/ ├── appservice_types.go ├── doc.go -├── register.go +└── register.go $ operator-sdk generate crds INFO[0000] Running CRD generation for Custom Resource group versions: [app:[v1alpha1], ] @@ -154,7 +197,7 @@ INFO[0001] CRD generation complete. $ tree deploy/crds/ deploy/crds -├── app.example.com_appservices_crd.yaml +└── app.example.com_appservices_crd.yaml ``` ## olm-catalog @@ -574,4 +617,5 @@ status of each of those resources as they exist in the cluster. [utility_link]: https://github.com/operator-framework/operator-sdk/blob/89bf021063d18b6769bdc551ed08fc37027939d5/pkg/util/k8sutil/k8sutil.go#L140 [k8s-code-generator]: https://github.com/kubernetes/code-generator +[openapi-code-generator]: https://github.com/kubernetes/kube-openapi [helm-user-guide-create-project]: https://github.com/operator-framework/operator-sdk/blob/master/doc/helm/user-guide.md#create-a-new-project diff --git a/go.mod b/go.mod index 225d84c44ee..1e425f54fae 100644 --- a/go.mod +++ b/go.mod @@ -56,7 +56,7 @@ require ( k8s.io/gengo v0.0.0-20191010091904-7fa3014cb28f k8s.io/helm v2.16.1+incompatible k8s.io/klog v1.0.0 - k8s.io/kube-openapi v0.0.0-20190918143330-0270cf2f1c1d // indirect + k8s.io/kube-openapi v0.0.0-20190918143330-0270cf2f1c1d k8s.io/kube-state-metrics v1.7.2 k8s.io/kubectl v0.0.0 k8s.io/kubernetes v1.16.2