From ce9590a8d187896a13190b9f865fe22b5c937ec3 Mon Sep 17 00:00:00 2001 From: Joe Lanford Date: Mon, 16 Mar 2020 16:07:17 -0400 Subject: [PATCH 1/2] cmd/operator-sdk: add --crd-version flag to relevant commands --- cmd/operator-sdk/add/api.go | 6 +- cmd/operator-sdk/add/crd.go | 4 +- cmd/operator-sdk/generate/crds.go | 12 +- cmd/operator-sdk/generate/openapi.go | 14 +- cmd/operator-sdk/internal/genutil/crds.go | 4 +- cmd/operator-sdk/new/cmd.go | 12 +- doc/cli/operator-sdk_add_api.md | 1 + doc/cli/operator-sdk_add_crd.md | 1 + doc/cli/operator-sdk_generate_crds.md | 3 +- doc/cli/operator-sdk_new.md | 1 + internal/generate/crd/crd.go | 52 ++++- internal/generate/crd/crd_test.go | 215 +++++++++++++++--- .../cache.example.com_memcachedrs_crd.yaml | 57 +++++ .../cache.example.com_memcacheds_crd.yaml | 56 +++++ ...che.example.com_v1alpha1_memcached_cr.yaml | 0 ...e.example.com_v1alpha1_memcachedrs_cr.yaml | 0 .../cache.example.com_memcachedrs_crd.yaml | 0 .../cache.example.com_memcacheds_crd.yaml | 0 ...che.example.com_v1alpha1_memcached_cr.yaml | 7 + ...e.example.com_v1alpha1_memcachedrs_cr.yaml | 6 + .../en/docs/cli/operator-sdk_add_api.md | 1 + .../en/docs/cli/operator-sdk_add_crd.md | 1 + .../en/docs/cli/operator-sdk_generate_crds.md | 3 +- .../content/en/docs/cli/operator-sdk_new.md | 1 + 24 files changed, 396 insertions(+), 61 deletions(-) create mode 100644 internal/generate/testdata/go/deploy/crds_v1/cache.example.com_memcachedrs_crd.yaml create mode 100644 internal/generate/testdata/go/deploy/crds_v1/cache.example.com_memcacheds_crd.yaml rename internal/generate/testdata/go/deploy/{crds => crds_v1}/cache.example.com_v1alpha1_memcached_cr.yaml (100%) rename internal/generate/testdata/go/deploy/{crds => crds_v1}/cache.example.com_v1alpha1_memcachedrs_cr.yaml (100%) rename internal/generate/testdata/go/deploy/{crds => crds_v1beta1}/cache.example.com_memcachedrs_crd.yaml (100%) rename internal/generate/testdata/go/deploy/{crds => crds_v1beta1}/cache.example.com_memcacheds_crd.yaml (100%) create mode 100644 internal/generate/testdata/go/deploy/crds_v1beta1/cache.example.com_v1alpha1_memcached_cr.yaml create mode 100644 internal/generate/testdata/go/deploy/crds_v1beta1/cache.example.com_v1alpha1_memcachedrs_cr.yaml diff --git a/cmd/operator-sdk/add/api.go b/cmd/operator-sdk/add/api.go index 25910e95742..b40d4f25b0b 100644 --- a/cmd/operator-sdk/add/api.go +++ b/cmd/operator-sdk/add/api.go @@ -21,6 +21,7 @@ import ( "path/filepath" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/internal/genutil" + gencrd "github.com/operator-framework/operator-sdk/internal/generate/crd" "github.com/operator-framework/operator-sdk/internal/scaffold" "github.com/operator-framework/operator-sdk/internal/scaffold/input" "github.com/operator-framework/operator-sdk/internal/util/projutil" @@ -33,6 +34,7 @@ var ( apiVersion string kind string skipGeneration bool + crdVersion string ) func newAddAPICmd() *cobra.Command { @@ -84,6 +86,8 @@ Example: } apiCmd.Flags().BoolVar(&skipGeneration, "skip-generation", false, "Skip generation of deepcopy and OpenAPI code and OpenAPI CRD specs") + apiCmd.Flags().StringVar(&crdVersion, "crd-version", gencrd.DefaultCRDVersion, + "CRD version to generate") return apiCmd } @@ -143,7 +147,7 @@ func apiRun(cmd *cobra.Command, args []string) error { } // Generate a validation spec for the new CRD. - if err := genutil.CRDGen(); err != nil { + if err := genutil.CRDGen(crdVersion); err != nil { log.Fatal(err) } } diff --git a/cmd/operator-sdk/add/crd.go b/cmd/operator-sdk/add/crd.go index 6ce1d5674c0..e472c85a155 100644 --- a/cmd/operator-sdk/add/crd.go +++ b/cmd/operator-sdk/add/crd.go @@ -56,6 +56,8 @@ Generated CR filename: /deploy/crds/__ if err := crdCmd.MarkFlagRequired("kind"); err != nil { log.Fatalf("Failed to mark `kind` flag for `add crd` subcommand as required") } + crdCmd.Flags().StringVar(&crdVersion, "crd-version", gencrd.DefaultCRDVersion, + "CRD version to generate") return crdCmd } @@ -101,7 +103,7 @@ func crdFunc(cmd *cobra.Command, args []string) error { // This command does not consider an APIs dir. Instead it adds a plain CRD // for the provided resource. We can use NewCRDNonGo to get this behavior. gcfg := gen.Config{} - crd := gencrd.NewCRDNonGo(gcfg, *resource) + crd := gencrd.NewCRDNonGo(gcfg, *resource, crdVersion) if err := crd.Generate(); err != nil { log.Fatalf("Error generating CRD for %s: %w", resource, err) } diff --git a/cmd/operator-sdk/generate/crds.go b/cmd/operator-sdk/generate/crds.go index 80640d71c65..fcf0f6ca037 100644 --- a/cmd/operator-sdk/generate/crds.go +++ b/cmd/operator-sdk/generate/crds.go @@ -18,13 +18,18 @@ import ( "fmt" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/internal/genutil" + gencrd "github.com/operator-framework/operator-sdk/internal/generate/crd" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) +var ( + crdVersion string +) + func newGenerateCRDsCmd() *cobra.Command { - return &cobra.Command{ + cmd := &cobra.Command{ Use: "crds", Short: "Generates CRDs for API's", Long: `generate crds generates CRDs or updates them if they exist, @@ -40,6 +45,9 @@ Example: `, RunE: crdsFunc, } + + cmd.Flags().StringVar(&crdVersion, "crd-version", gencrd.DefaultCRDVersion, "CRD version to generate") + return cmd } func crdsFunc(cmd *cobra.Command, args []string) error { @@ -49,7 +57,7 @@ func crdsFunc(cmd *cobra.Command, args []string) error { // Skip usage printing on error, since this command will never fail from // improper CLI usage. - if err := genutil.CRDGen(); err != nil { + if err := genutil.CRDGen(crdVersion); err != nil { log.Fatal(err) } return nil diff --git a/cmd/operator-sdk/generate/openapi.go b/cmd/operator-sdk/generate/openapi.go index daddc8fb341..2ca4e3c1a04 100644 --- a/cmd/operator-sdk/generate/openapi.go +++ b/cmd/operator-sdk/generate/openapi.go @@ -72,14 +72,14 @@ func openAPIFunc(cmd *cobra.Command, args []string) error { return fmt.Errorf("command %s doesn't accept any arguments", cmd.CommandPath()) } - fs := []func() error{ - genutil.OpenAPIGen, - genutil.CRDGen, + if err := genutil.OpenAPIGen(); err != nil { + log.Fatal(err) } - for _, f := range fs { - if err := f(); err != nil { - log.Fatal(err) - } + + // Hardcode "v1beta1" here because we never want to change the functionality of this deprecated function. + if err := genutil.CRDGen("v1beta1"); err != nil { + log.Fatal(err) } + return nil } diff --git a/cmd/operator-sdk/internal/genutil/crds.go b/cmd/operator-sdk/internal/genutil/crds.go index a6f235a4a12..9fba6510c8a 100644 --- a/cmd/operator-sdk/internal/genutil/crds.go +++ b/cmd/operator-sdk/internal/genutil/crds.go @@ -26,13 +26,13 @@ import ( ) // CRDGen generates CRDs for all APIs in pkg/apis. -func CRDGen() error { +func CRDGen(crdVersion string) error { projutil.MustInProjectRoot() log.Info("Running CRD generator.") cfg := gen.Config{} - crd := gencrd.NewCRDGo(cfg) + crd := gencrd.NewCRDGo(cfg, crdVersion) if err := crd.Generate(); err != nil { return fmt.Errorf("error generating CRDs from APIs in %s: %w", scaffold.ApisDir, err) } diff --git a/cmd/operator-sdk/new/cmd.go b/cmd/operator-sdk/new/cmd.go index 054d4d8c817..a37363742a1 100644 --- a/cmd/operator-sdk/new/cmd.go +++ b/cmd/operator-sdk/new/cmd.go @@ -123,6 +123,8 @@ generates a default directory layout based on the input . "Specific version of the helm chart (default is latest version)") newCmd.Flags().StringVar(&helmChartRepo, "helm-chart-repo", "", "Chart repository URL for the requested helm chart") + newCmd.Flags().StringVar(&crdVersion, "crd-version", gencrd.DefaultCRDVersion, + "CRD version to generate (Only used for --type=ansible|helm)") return newCmd } @@ -142,6 +144,8 @@ var ( helmChartRef string helmChartVersion string helmChartRepo string + + crdVersion string ) func newFunc(cmd *cobra.Command, args []string) error { @@ -321,7 +325,7 @@ func doAnsibleScaffold() error { return fmt.Errorf("new ansible scaffold failed: %v", err) } - if err = generateCRDNonGo(projectName, *resource); err != nil { + if err = generateCRDNonGo(projectName, *resource, crdVersion); err != nil { return err } @@ -411,7 +415,7 @@ func doHelmScaffold() error { return fmt.Errorf("new helm scaffold failed: %v", err) } - if err = generateCRDNonGo(projectName, *resource); err != nil { + if err = generateCRDNonGo(projectName, *resource, crdVersion); err != nil { return err } @@ -422,13 +426,13 @@ func doHelmScaffold() error { return nil } -func generateCRDNonGo(projectName string, resource scaffold.Resource) error { +func generateCRDNonGo(projectName string, resource scaffold.Resource, crdVersion string) error { crdsDir := filepath.Join(projectName, scaffold.CRDsDir) gcfg := gen.Config{ Inputs: map[string]string{gencrd.CRDsDirKey: crdsDir}, OutputDir: crdsDir, } - crd := gencrd.NewCRDNonGo(gcfg, resource) + crd := gencrd.NewCRDNonGo(gcfg, resource, crdVersion) if err := crd.Generate(); err != nil { return fmt.Errorf("error generating CRD for %s: %w", resource, err) } diff --git a/doc/cli/operator-sdk_add_api.md b/doc/cli/operator-sdk_add_api.md index bc68d84d3f4..611e893d75e 100644 --- a/doc/cli/operator-sdk_add_api.md +++ b/doc/cli/operator-sdk_add_api.md @@ -44,6 +44,7 @@ operator-sdk add api [flags] ``` --api-version string Kubernetes APIVersion that has a format of $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1) + --crd-version string CRD version to generate (default "v1beta1") -h, --help help for api --kind string Kubernetes resource Kind name. (e.g AppService) --skip-generation Skip generation of deepcopy and OpenAPI code and OpenAPI CRD specs diff --git a/doc/cli/operator-sdk_add_crd.md b/doc/cli/operator-sdk_add_crd.md index 2b078675a79..90957cc4ee5 100644 --- a/doc/cli/operator-sdk_add_crd.md +++ b/doc/cli/operator-sdk_add_crd.md @@ -21,6 +21,7 @@ operator-sdk add crd [flags] ``` --api-version string Kubernetes apiVersion and has a format of $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1) + --crd-version string CRD version to generate (default "v1beta1") -h, --help help for crd --kind string Kubernetes CustomResourceDefintion kind. (e.g AppService) ``` diff --git a/doc/cli/operator-sdk_generate_crds.md b/doc/cli/operator-sdk_generate_crds.md index 9c2f98d392c..b63b9472c7d 100644 --- a/doc/cli/operator-sdk_generate_crds.md +++ b/doc/cli/operator-sdk_generate_crds.md @@ -23,7 +23,8 @@ operator-sdk generate crds [flags] ### Options ``` - -h, --help help for crds + --crd-version string CRD version to generate (default "v1beta1") + -h, --help help for crds ``` ### SEE ALSO diff --git a/doc/cli/operator-sdk_new.md b/doc/cli/operator-sdk_new.md index 27759248175..b8fb22a19d3 100644 --- a/doc/cli/operator-sdk_new.md +++ b/doc/cli/operator-sdk_new.md @@ -67,6 +67,7 @@ operator-sdk new [flags] ``` --api-version string Kubernetes apiVersion and has a format of $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1) - used with "ansible" or "helm" types + --crd-version string CRD version to generate (Only used for --type=ansible|helm) (default "v1beta1") --generate-playbook Generate a playbook skeleton. (Only used for --type ansible) --git-init Initialize the project directory as a git repository (default false) --header-file string Path to file containing headers for generated Go files. Copied to hack/boilerplate.go.txt diff --git a/internal/generate/crd/crd.go b/internal/generate/crd/crd.go index 3479371e77a..cc24e72e2d9 100644 --- a/internal/generate/crd/crd.go +++ b/internal/generate/crd/crd.go @@ -23,7 +23,7 @@ import ( "sort" "strings" - gen "github.com/operator-framework/operator-sdk/internal/generate/gen" + "github.com/operator-framework/operator-sdk/internal/generate/gen" "github.com/operator-framework/operator-sdk/internal/scaffold" "github.com/operator-framework/operator-sdk/internal/util/fileutil" "github.com/operator-framework/operator-sdk/internal/util/k8sutil" @@ -32,6 +32,8 @@ import ( "github.com/ghodss/yaml" log "github.com/sirupsen/logrus" "github.com/spf13/afero" + apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" + apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -45,20 +47,23 @@ type crdGenerator struct { isOperatorGo bool // resource contains API information used to configure single-CRD generation. // This is only required when isOperatorGo is false. - resource scaffold.Resource + resource scaffold.Resource + crdVersion string } const ( - APIsDirKey = "apis" - CRDsDirKey = "crds" + APIsDirKey = "apis" + CRDsDirKey = "crds" + DefaultCRDVersion = "v1beta1" ) // NewCRDGo returns a CRD generator configured to generate CustomResourceDefintion // manifests from Go API files. -func NewCRDGo(cfg gen.Config) gen.Generator { +func NewCRDGo(cfg gen.Config, crdVersion string) gen.Generator { g := crdGenerator{ Config: cfg, isOperatorGo: true, + crdVersion: crdVersion, } if g.Inputs == nil { g.Inputs = map[string]string{} @@ -77,11 +82,12 @@ func NewCRDGo(cfg gen.Config) gen.Generator { // NewCRDNonGo returns a CRD generator configured to generate a // CustomResourceDefintion manifest from scratch using data in resource. -func NewCRDNonGo(cfg gen.Config, resource scaffold.Resource) gen.Generator { +func NewCRDNonGo(cfg gen.Config, resource scaffold.Resource, crdVersion string) gen.Generator { g := crdGenerator{ Config: cfg, resource: resource, isOperatorGo: false, + crdVersion: crdVersion, } if g.Inputs == nil { g.Inputs = map[string]string{} @@ -115,6 +121,11 @@ func (g crdGenerator) validate() error { return fmt.Errorf("resource is invalid: %w", err) } } + switch g.crdVersion { + case "v1", "v1beta1": + default: + return fmt.Errorf("crd version %q is invalid", g.crdVersion) + } return nil } @@ -156,10 +167,11 @@ func (g crdGenerator) generateGo() (map[string][]byte, error) { defName := "output:crd:cache" cacheOutputDir := filepath.Clean(g.OutputDir) rawOpts := []string{ - "crd", + fmt.Sprintf("crd:crdVersions={%s}", g.crdVersion), fmt.Sprintf("paths=%s/...", fileutil.DotPath(g.Inputs[APIsDirKey])), fmt.Sprintf("%s:dir=%s", defName, cacheOutputDir), } + runner := gen.NewCachedRunner() runner.AddOutputRule(defName, gen.OutputToCachedDirectory{}) if err := runner.Run(rawOpts); err != nil { @@ -268,7 +280,31 @@ func (g crdGenerator) generateNonGo() (map[string][]byte, error) { if err := checkCRDVersions(crd); err != nil { return nil, fmt.Errorf("error checking CRD %s versions: %w", crd.GetName(), err) } - b, err := k8sutil.GetObjectBytes(&crd, yaml.Marshal) + + var ( + b []byte + err error + ) + switch g.crdVersion { + case "v1beta1": + crd.TypeMeta.APIVersion = apiextv1beta1.SchemeGroupVersion.String() + b, err = k8sutil.GetObjectBytes(&crd, yaml.Marshal) + case "v1": + var unversioned apiext.CustomResourceDefinition + //nolint:lll + if err := apiextv1beta1.Convert_v1beta1_CustomResourceDefinition_To_apiextensions_CustomResourceDefinition(&crd, &unversioned, nil); err != nil { + return nil, err + } + var out apiextv1.CustomResourceDefinition + out.TypeMeta.APIVersion = apiextv1.SchemeGroupVersion.String() + out.TypeMeta.Kind = "CustomResourceDefinition" + //nolint:lll + if err := apiextv1.Convert_apiextensions_CustomResourceDefinition_To_v1_CustomResourceDefinition(&unversioned, &out, nil); err != nil { + return nil, err + } + b, err = k8sutil.GetObjectBytes(&out, yaml.Marshal) + } + if err != nil { return nil, fmt.Errorf("error marshalling CRD %s: %w", crd.GetName(), err) } diff --git a/internal/generate/crd/crd_test.go b/internal/generate/crd/crd_test.go index 85fbda6da43..98824ed3a2b 100644 --- a/internal/generate/crd/crd_test.go +++ b/internal/generate/crd/crd_test.go @@ -22,6 +22,7 @@ import ( "path" "path/filepath" "strconv" + "strings" "testing" "time" @@ -72,31 +73,57 @@ func TestGenerate(t *testing.T) { cases := []struct { description string generator gen.Generator + wantErr bool }{ { - "Generate Go CRD", - NewCRDGo(gen.Config{ + description: "Generate Go CRD", + generator: NewCRDGo(gen.Config{ Inputs: map[string]string{ APIsDirKey: filepath.Join(testGoDataDir, scaffold.ApisDir), }, OutputDir: filepath.Join(tmp, randomString()), - }), + }, "v1beta1"), + wantErr: false, }, { - "Generate non-Go CRD", - NewCRDNonGo(gen.Config{ + description: "Generate non-Go CRD", + generator: NewCRDNonGo(gen.Config{ Inputs: map[string]string{ APIsDirKey: filepath.Join(testGoDataDir, scaffold.ApisDir), }, OutputDir: filepath.Join(tmp, randomString()), - }, *r), + }, *r, "v1beta1"), + wantErr: false, + }, + { + description: "invalid Go CRD version", + generator: NewCRDGo(gen.Config{ + Inputs: map[string]string{ + APIsDirKey: filepath.Join(testGoDataDir, scaffold.ApisDir), + }, + OutputDir: filepath.Join(tmp, randomString()), + }, "invalid"), + wantErr: true, + }, + { + description: "invalid non-Go CRD version", + generator: NewCRDNonGo(gen.Config{ + Inputs: map[string]string{ + APIsDirKey: filepath.Join(testGoDataDir, scaffold.ApisDir), + }, + OutputDir: filepath.Join(tmp, randomString()), + }, *r, "invalid"), + wantErr: true, }, } for _, c := range cases { t.Run(c.description, func(t *testing.T) { - err = c.generator.Generate() + err := c.generator.Generate() if err != nil { + if c.wantErr { + return + } t.Errorf("Wanted nil error, got: %v", err) } }) @@ -109,52 +136,82 @@ func TestCRDGo(t *testing.T) { APIsDirKey: filepath.Join(testGoDataDir, scaffold.ApisDir), }, } - g := NewCRDGo(cfg) - fileMap, err := g.(crdGenerator).generateGo() - if err != nil { - t.Fatalf("Failed to execute CRD generator: %v", err) - } + r, err := scaffold.NewResource(testAPIVersion, testKind) if err != nil { t.Fatal(err) } - if b, ok := fileMap[getFileNameForResource(*r)]; !ok { - t.Errorf("Failed to generate CRD for %s", r) - } else { - assert.Equal(t, crdCustomExp, string(b)) + + cases := []struct { + crdVersion string + expectedCRD string + }{ + {"v1beta1", crdCustomExpV1beta1}, + {"v1", crdCustomExpV1}, + } + + for _, c := range cases { + t.Run(c.crdVersion, func(t *testing.T) { + g := NewCRDGo(cfg, c.crdVersion) + fileMap, err := g.(crdGenerator).generateGo() + if err != nil { + t.Fatalf("Failed to execute CRD generator: %v", err) + } + if b, ok := fileMap[getFileNameForResource(*r)]; !ok { + t.Errorf("Failed to generate CRDs for %s", r) + } else { + assert.Equal(t, c.expectedCRD, string(b)) + } + }) } } func TestCRDNonGo(t *testing.T) { + r, err := scaffold.NewResource(testAPIVersion, testKind) + if err != nil { + t.Fatal(err) + } + cases := []struct { - description string - apiVersion, kind string - crdsDir string - expCRD string - wantErr bool + description string + crdsDir string + crdVersion string + expCRD string }{ { - "non-existent CRD with default structural schema", - testAPIVersion, testKind, filepath.Join("not", "exist"), crdNonGoDefaultExp, false, + "non-existent v1beta1 CRD with default structural schema", + filepath.Join("not", "exist"), "v1beta1", crdNonGoDefaultExpV1beta1, + }, + { + "non-existent v1 CRD with default structural schema", + filepath.Join("not", "exist"), "v1", crdNonGoDefaultExpV1, + }, + { + "existing v1beta1 CRD with custom structural schema", + filepath.Join(testGoDataDir, scaffold.CRDsDir+"_v1beta1"), "v1beta1", crdCustomExpV1beta1, + }, + { + "existing v1 CRD with custom structural schema", + filepath.Join(testGoDataDir, scaffold.CRDsDir+"_v1"), "v1", crdCustomExpV1, + }, + { + "existing v1beta1 to v1 CRD with custom structural schema", + filepath.Join(testGoDataDir, scaffold.CRDsDir+"_v1beta1"), "v1", crdCustomExpV1, }, { - "existing CRD with custom structural schema", - testAPIVersion, testKind, filepath.Join(testGoDataDir, scaffold.CRDsDir), crdCustomExp, false, + "existing v1 to v1beta1 CRD with custom structural schema", + filepath.Join(testGoDataDir, scaffold.CRDsDir+"_v1"), "v1beta1", asV1beta1(crdCustomExpV1), }, } for _, c := range cases { t.Run(c.description, func(t *testing.T) { - r, err := scaffold.NewResource(c.apiVersion, c.kind) - if err != nil { - t.Fatal(err) - } cfg := gen.Config{ Inputs: map[string]string{ CRDsDirKey: c.crdsDir, }, } - g := NewCRDNonGo(cfg, *r) + g := NewCRDNonGo(cfg, *r, c.crdVersion) fileMap, err := g.(crdGenerator).generateNonGo() if err != nil { t.Fatalf("Error executing CRD generator: %v", err) @@ -168,9 +225,13 @@ func TestCRDNonGo(t *testing.T) { } } -// crdNonGoDefaultExp is the default non-go CRD. Non-go projects don't have the +func asV1beta1(crdV1 string) string { + return strings.ReplaceAll(crdV1, "apiVersion: apiextensions.k8s.io/v1", "apiVersion: apiextensions.k8s.io/v1beta1") +} + +// crdNonGoDefaultExpV1beta1 is the default non-go v1beta1 CRD. Non-go projects don't have the // luxury of kubebuilder annotations. -const crdNonGoDefaultExp = `apiVersion: apiextensions.k8s.io/v1beta1 +const crdNonGoDefaultExpV1beta1 = `apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: memcacheds.cache.example.com @@ -194,9 +255,35 @@ spec: storage: true ` -// crdCustomExp is a CRD with custom validation, either created manually or +// crdNonGoDefaultExpV1 is the equivalent default non-go v1 CRD. Non-go projects don't have the +// luxury of kubebuilder annotations. +const crdNonGoDefaultExpV1 = `apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: memcacheds.cache.example.com +spec: + group: cache.example.com + names: + kind: Memcached + listKind: MemcachedList + plural: memcacheds + singular: memcached + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + type: object + x-kubernetes-preserve-unknown-fields: true + served: true + storage: true + subresources: + status: {} +` + +// crdCustomExpV1beta1 is a v1beta1 CRD with custom validation, either created manually or // with Go API code annotations. -const crdCustomExp = `apiVersion: apiextensions.k8s.io/v1beta1 +const crdCustomExpV1beta1 = `apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: memcacheds.cache.example.com @@ -254,3 +341,63 @@ spec: served: true storage: true ` + +// crdCustomExpV1 is the equivalent v1 CRD with custom validation, either created manually or +// with Go API code annotations. +const crdCustomExpV1 = `apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: memcacheds.cache.example.com +spec: + group: cache.example.com + names: + kind: Memcached + listKind: MemcachedList + plural: memcacheds + singular: memcached + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Memcached is the Schema for the memcacheds API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: MemcachedSpec defines the desired state of Memcached + properties: + size: + description: Size is the size of the memcached deployment + format: int32 + type: integer + required: + - size + type: object + status: + description: MemcachedStatus defines the observed state of Memcached + properties: + nodes: + description: Nodes are the names of the memcached pods + items: + type: string + type: array + required: + - nodes + type: object + type: object + served: true + storage: true + subresources: + status: {} +` diff --git a/internal/generate/testdata/go/deploy/crds_v1/cache.example.com_memcachedrs_crd.yaml b/internal/generate/testdata/go/deploy/crds_v1/cache.example.com_memcachedrs_crd.yaml new file mode 100644 index 00000000000..be7b00dfc12 --- /dev/null +++ b/internal/generate/testdata/go/deploy/crds_v1/cache.example.com_memcachedrs_crd.yaml @@ -0,0 +1,57 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: memcachedrs.cache.example.com +spec: + group: cache.example.com + names: + kind: MemcachedRS + listKind: MemcachedRSList + plural: memcachedrs + singular: memcachedrs + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: MemcachedRS is the Schema for the memcachedrs API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: MemcachedRSSpec defines the desired state of MemcachedRS + properties: + numNodes: + format: int32 + type: integer + required: + - numNodes + type: object + status: + description: MemcachedRSStatus defines the observed state of MemcachedRS + properties: + nodeList: + items: + type: string + type: array + test: + type: boolean + required: + - nodeList + - test + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/internal/generate/testdata/go/deploy/crds_v1/cache.example.com_memcacheds_crd.yaml b/internal/generate/testdata/go/deploy/crds_v1/cache.example.com_memcacheds_crd.yaml new file mode 100644 index 00000000000..800a156069e --- /dev/null +++ b/internal/generate/testdata/go/deploy/crds_v1/cache.example.com_memcacheds_crd.yaml @@ -0,0 +1,56 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: memcacheds.cache.example.com +spec: + group: cache.example.com + names: + kind: Memcached + listKind: MemcachedList + plural: memcacheds + singular: memcached + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Memcached is the Schema for the memcacheds API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: MemcachedSpec defines the desired state of Memcached + properties: + size: + description: Size is the size of the memcached deployment + format: int32 + type: integer + required: + - size + type: object + status: + description: MemcachedStatus defines the observed state of Memcached + properties: + nodes: + description: Nodes are the names of the memcached pods + items: + type: string + type: array + required: + - nodes + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/internal/generate/testdata/go/deploy/crds/cache.example.com_v1alpha1_memcached_cr.yaml b/internal/generate/testdata/go/deploy/crds_v1/cache.example.com_v1alpha1_memcached_cr.yaml similarity index 100% rename from internal/generate/testdata/go/deploy/crds/cache.example.com_v1alpha1_memcached_cr.yaml rename to internal/generate/testdata/go/deploy/crds_v1/cache.example.com_v1alpha1_memcached_cr.yaml diff --git a/internal/generate/testdata/go/deploy/crds/cache.example.com_v1alpha1_memcachedrs_cr.yaml b/internal/generate/testdata/go/deploy/crds_v1/cache.example.com_v1alpha1_memcachedrs_cr.yaml similarity index 100% rename from internal/generate/testdata/go/deploy/crds/cache.example.com_v1alpha1_memcachedrs_cr.yaml rename to internal/generate/testdata/go/deploy/crds_v1/cache.example.com_v1alpha1_memcachedrs_cr.yaml diff --git a/internal/generate/testdata/go/deploy/crds/cache.example.com_memcachedrs_crd.yaml b/internal/generate/testdata/go/deploy/crds_v1beta1/cache.example.com_memcachedrs_crd.yaml similarity index 100% rename from internal/generate/testdata/go/deploy/crds/cache.example.com_memcachedrs_crd.yaml rename to internal/generate/testdata/go/deploy/crds_v1beta1/cache.example.com_memcachedrs_crd.yaml diff --git a/internal/generate/testdata/go/deploy/crds/cache.example.com_memcacheds_crd.yaml b/internal/generate/testdata/go/deploy/crds_v1beta1/cache.example.com_memcacheds_crd.yaml similarity index 100% rename from internal/generate/testdata/go/deploy/crds/cache.example.com_memcacheds_crd.yaml rename to internal/generate/testdata/go/deploy/crds_v1beta1/cache.example.com_memcacheds_crd.yaml diff --git a/internal/generate/testdata/go/deploy/crds_v1beta1/cache.example.com_v1alpha1_memcached_cr.yaml b/internal/generate/testdata/go/deploy/crds_v1beta1/cache.example.com_v1alpha1_memcached_cr.yaml new file mode 100644 index 00000000000..2b8f17c3998 --- /dev/null +++ b/internal/generate/testdata/go/deploy/crds_v1beta1/cache.example.com_v1alpha1_memcached_cr.yaml @@ -0,0 +1,7 @@ +apiVersion: cache.example.com/v1alpha1 +kind: Memcached +metadata: + name: example-memcached +spec: + # Add fields here + size: 3 diff --git a/internal/generate/testdata/go/deploy/crds_v1beta1/cache.example.com_v1alpha1_memcachedrs_cr.yaml b/internal/generate/testdata/go/deploy/crds_v1beta1/cache.example.com_v1alpha1_memcachedrs_cr.yaml new file mode 100644 index 00000000000..92861ed5fe5 --- /dev/null +++ b/internal/generate/testdata/go/deploy/crds_v1beta1/cache.example.com_v1alpha1_memcachedrs_cr.yaml @@ -0,0 +1,6 @@ +apiVersion: cache.example.com/v1alpha1 +kind: MemcachedRS +metadata: + name: example-memcachedrs +spec: + numNodes: 4 diff --git a/website/content/en/docs/cli/operator-sdk_add_api.md b/website/content/en/docs/cli/operator-sdk_add_api.md index bc68d84d3f4..611e893d75e 100644 --- a/website/content/en/docs/cli/operator-sdk_add_api.md +++ b/website/content/en/docs/cli/operator-sdk_add_api.md @@ -44,6 +44,7 @@ operator-sdk add api [flags] ``` --api-version string Kubernetes APIVersion that has a format of $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1) + --crd-version string CRD version to generate (default "v1beta1") -h, --help help for api --kind string Kubernetes resource Kind name. (e.g AppService) --skip-generation Skip generation of deepcopy and OpenAPI code and OpenAPI CRD specs diff --git a/website/content/en/docs/cli/operator-sdk_add_crd.md b/website/content/en/docs/cli/operator-sdk_add_crd.md index 2b078675a79..90957cc4ee5 100644 --- a/website/content/en/docs/cli/operator-sdk_add_crd.md +++ b/website/content/en/docs/cli/operator-sdk_add_crd.md @@ -21,6 +21,7 @@ operator-sdk add crd [flags] ``` --api-version string Kubernetes apiVersion and has a format of $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1) + --crd-version string CRD version to generate (default "v1beta1") -h, --help help for crd --kind string Kubernetes CustomResourceDefintion kind. (e.g AppService) ``` diff --git a/website/content/en/docs/cli/operator-sdk_generate_crds.md b/website/content/en/docs/cli/operator-sdk_generate_crds.md index 9c2f98d392c..b63b9472c7d 100644 --- a/website/content/en/docs/cli/operator-sdk_generate_crds.md +++ b/website/content/en/docs/cli/operator-sdk_generate_crds.md @@ -23,7 +23,8 @@ operator-sdk generate crds [flags] ### Options ``` - -h, --help help for crds + --crd-version string CRD version to generate (default "v1beta1") + -h, --help help for crds ``` ### SEE ALSO diff --git a/website/content/en/docs/cli/operator-sdk_new.md b/website/content/en/docs/cli/operator-sdk_new.md index 27759248175..b8fb22a19d3 100644 --- a/website/content/en/docs/cli/operator-sdk_new.md +++ b/website/content/en/docs/cli/operator-sdk_new.md @@ -67,6 +67,7 @@ operator-sdk new [flags] ``` --api-version string Kubernetes apiVersion and has a format of $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1) - used with "ansible" or "helm" types + --crd-version string CRD version to generate (Only used for --type=ansible|helm) (default "v1beta1") --generate-playbook Generate a playbook skeleton. (Only used for --type ansible) --git-init Initialize the project directory as a git repository (default false) --header-file string Path to file containing headers for generated Go files. Copied to hack/boilerplate.go.txt From 9db5a510661b7c01b572820aa1fdf85ebe6572bb Mon Sep 17 00:00:00 2001 From: Joe Lanford Date: Tue, 24 Mar 2020 11:54:25 -0400 Subject: [PATCH 2/2] CHANGELOG.md: added line for #2684 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c68da8c4baa..6557c5f9204 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - The flag `--watch-namespace` and `--operator-namespace` was added to `operator-sdk run --local`, `operator-sdk test --local` and `operator-sdk cleanup` commands in order to replace the flag `--namespace` which was deprecated.([#2617](https://github.com/operator-framework/operator-sdk/pull/2617)) - The methods `ctx.GetOperatorNamespace()` and `ctx.GetWatchNamespace()` was added `pkg/test` in order to replace `ctx.GetNamespace()` which is deprecated. ([#2617](https://github.com/operator-framework/operator-sdk/pull/2617)) +- The `--crd-version` flag was added to the `new`, `add api`, `add crd`, and `generate crds` commands so that users can opt-in to `v1` CRDs. ([#2684](https://github.com/operator-framework/operator-sdk/pull/2684)) ### Changed