Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 5 additions & 1 deletion cmd/operator-sdk/add/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -33,6 +34,7 @@ var (
apiVersion string
kind string
skipGeneration bool
crdVersion string
)

func newAddAPICmd() *cobra.Command {
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
}
}
Expand Down
4 changes: 3 additions & 1 deletion cmd/operator-sdk/add/crd.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ Generated CR filename: <project-name>/deploy/crds/<full group>_<version>_<kind>
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
}

Expand Down Expand Up @@ -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)
}
Expand Down
12 changes: 10 additions & 2 deletions cmd/operator-sdk/generate/crds.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 {
Expand All @@ -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
Expand Down
14 changes: 7 additions & 7 deletions cmd/operator-sdk/generate/openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
4 changes: 2 additions & 2 deletions cmd/operator-sdk/internal/genutil/crds.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
12 changes: 8 additions & 4 deletions cmd/operator-sdk/new/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ generates a default directory layout based on the input <project-name>.
"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
}
Expand All @@ -142,6 +144,8 @@ var (
helmChartRef string
helmChartVersion string
helmChartRepo string

crdVersion string
)

func newFunc(cmd *cobra.Command, args []string) error {
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
}

Expand All @@ -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)
}
Expand Down
1 change: 1 addition & 0 deletions doc/cli/operator-sdk_add_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions doc/cli/operator-sdk_add_crd.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
```
Expand Down
3 changes: 2 additions & 1 deletion doc/cli/operator-sdk_generate_crds.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions doc/cli/operator-sdk_new.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ operator-sdk new <project-name> [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
Expand Down
52 changes: 44 additions & 8 deletions internal/generate/crd/crd.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand All @@ -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{}
Expand All @@ -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{}
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're going to switch to v1.CustomResourceDefinition by default in the future, we could set crd := apiextv1.CustomResourceDefinition{} above and return a apiextv1.CustomResourceDefinition from newCRDForResource(), then convert v1 -> v1beta. We'd also have to do some conversion if the file exists on disk and is v1beta1.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the reason I didn't do that was because of the possibility of a v1beta1 CRD on disk. v1 moved several top level fields from v1beta1 into the versions items, so it is not possible to unmarshal the v1beta1 YAML into a v1 struct.

However, the v1beta1 struct is a superset of the v1 struct, so it is possible to unmarshal v1 YAML into a v1beta1 struct and then convert back.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to use the generic apiextensions struct for both?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I properly understood the suggestion here, IHMO is better we keep each version implementation since in the future we will just remove the v1beta1 and keep all generating v1 only.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to use the generic apiextensions struct for both?

I can't get that to work either. For some reason (still trying to figure out why) everything gets marshaled without having the correct YAML layout (e.g. in the output there's actually an objectMeta key containing the GVK info, instead of that being inlined at the root).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve had trouble marshaling the generic CRD struct before. The current approach is fine then.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Once we bump to v1.19, we'll have to basically revert this PR and change from v1beta1 to v1. This function will basically go back to how it looked before, except with the v1 types.

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)
}
Expand Down
Loading