-
Notifications
You must be signed in to change notification settings - Fork 1.8k
generate: add bundle subcommand for new project layouts
#3065
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,209 @@ | ||
| // Copyright 2020 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 bundle | ||
|
|
||
| import ( | ||
| "errors" | ||
| "fmt" | ||
| "os" | ||
| "path/filepath" | ||
|
|
||
| "github.com/operator-framework/operator-registry/pkg/lib/bundle" | ||
| "sigs.k8s.io/kubebuilder/pkg/model/config" | ||
|
|
||
| genutil "github.com/operator-framework/operator-sdk/cmd/operator-sdk/generate/internal" | ||
| gencsv "github.com/operator-framework/operator-sdk/internal/generate/clusterserviceversion" | ||
| "github.com/operator-framework/operator-sdk/internal/generate/collector" | ||
| ) | ||
|
|
||
| // setCommonDefaults sets defaults useful to all modes of this subcommand. | ||
| func (c *bundleCmd) setCommonDefaults(cfg *config.Config) { | ||
| if c.operatorName == "" { | ||
| c.operatorName = filepath.Base(cfg.Repo) | ||
| } | ||
| } | ||
|
|
||
| // runKustomize generates kustomize bundle bases. | ||
| func (c bundleCmd) runKustomize(cfg *config.Config) error { | ||
|
|
||
| if !c.quiet { | ||
| fmt.Println("Generating bundle manifest kustomize bases") | ||
| } | ||
|
|
||
| defaultDir := filepath.Join("config", "bundle") | ||
| if c.inputDir == "" { | ||
| c.inputDir = defaultDir | ||
| } | ||
| if c.outputDir == "" { | ||
| c.outputDir = defaultDir | ||
| } | ||
| if c.apisDir == "" { | ||
| if cfg.MultiGroup { | ||
| c.apisDir = "apis" | ||
| } else { | ||
| c.apisDir = "api" | ||
| } | ||
| } | ||
|
|
||
| csvGen := gencsv.Generator{ | ||
| OperatorName: c.operatorName, | ||
| OperatorType: genutil.PluginKeyToOperatorType(cfg.Layout), | ||
| } | ||
| opts := []gencsv.Option{ | ||
| gencsv.WithBase(c.inputDir, c.apisDir), | ||
| gencsv.WithBaseWriter(c.outputDir), | ||
| } | ||
| if err := csvGen.Generate(cfg, opts...); err != nil { | ||
| return fmt.Errorf("error generating ClusterServiceVersion: %v", err) | ||
| } | ||
|
|
||
| if !c.quiet { | ||
| fmt.Println("Bases generated successfully in", c.outputDir) | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // validateManifests validates c for bundle manifests generation. | ||
| func (c bundleCmd) validateManifests(*config.Config) (err error) { | ||
| if c.version != "" { | ||
| if err := genutil.ValidateVersion(c.version); err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| if !genutil.IsPipeReader() { | ||
| if c.manifestRoot == "" { | ||
| return errors.New("--manifest-root must be set if not reading from stdin") | ||
| } | ||
| if c.crdsDir == "" { | ||
| return errors.New("--crds-dir must be set if not reading from stdin") | ||
| } | ||
| } | ||
|
|
||
| if c.stdout { | ||
| if c.outputDir != "" { | ||
| return errors.New("--output-dir cannot be set if writing to stdout") | ||
| } | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // runManifests generates bundle manifests. | ||
| func (c bundleCmd) runManifests(cfg *config.Config) (err error) { | ||
|
|
||
| if !c.quiet && !c.stdout { | ||
| if c.version == "" { | ||
| fmt.Println("Generating bundle manifests") | ||
| } else { | ||
| fmt.Println("Generating bundle manifests version", c.version) | ||
| } | ||
| } | ||
|
|
||
| defaultBundleDir := filepath.Join("config", "bundle") | ||
| if c.inputDir == "" { | ||
| c.inputDir = defaultBundleDir | ||
| } | ||
| if !c.stdout { | ||
| if c.outputDir == "" { | ||
| c.outputDir = defaultBundleDir | ||
| } | ||
| } | ||
| // Only regenerate API definitions once. | ||
| if c.apisDir == "" && !c.kustomize { | ||
| if cfg.MultiGroup { | ||
| c.apisDir = "apis" | ||
| } else { | ||
| c.apisDir = "api" | ||
| } | ||
| } | ||
|
|
||
| col := &collector.Manifests{} | ||
| if genutil.IsPipeReader() { | ||
| if err := col.UpdateFromReader(os.Stdin); err != nil { | ||
| return err | ||
| } | ||
| } | ||
| if c.manifestRoot != "" { | ||
| if err := col.UpdateFromDirs(c.manifestRoot, c.crdsDir); err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| csvGen := gencsv.Generator{ | ||
| OperatorName: c.operatorName, | ||
| OperatorType: genutil.PluginKeyToOperatorType(cfg.Layout), | ||
| Version: c.version, | ||
| Collector: col, | ||
| } | ||
|
|
||
| stdout := genutil.NewMultiManifestWriter(os.Stdout) | ||
| opts := []gencsv.Option{ | ||
| gencsv.WithBase(c.inputDir, c.apisDir), | ||
| } | ||
| if c.stdout { | ||
| opts = append(opts, gencsv.WithWriter(stdout)) | ||
| } else { | ||
| opts = append(opts, gencsv.WithBundleWriter(c.outputDir)) | ||
| } | ||
|
|
||
| if err := csvGen.Generate(cfg, opts...); err != nil { | ||
| return fmt.Errorf("error generating ClusterServiceVersion: %v", err) | ||
| } | ||
|
|
||
| if c.stdout { | ||
| if err := genutil.WriteCRDs(stdout, col.CustomResourceDefinitions...); err != nil { | ||
| return err | ||
| } | ||
| } else { | ||
| dir := filepath.Join(c.outputDir, bundle.ManifestsDir) | ||
| if err := genutil.WriteCRDFiles(dir, col.CustomResourceDefinitions...); err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| if !c.quiet && !c.stdout { | ||
| fmt.Println("Bundle manifests generated successfully in", c.outputDir) | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // runMetadata generates a bundle.Dockerfile and bundle metadata. | ||
| func (c bundleCmd) runMetadata() error { | ||
|
|
||
| directory := c.inputDir | ||
| if directory == "" { | ||
| directory = filepath.Join("config", "bundle", bundle.ManifestsDir) | ||
| } else { | ||
| directory = filepath.Join(directory, bundle.ManifestsDir) | ||
| } | ||
| outputDir := c.outputDir | ||
| if filepath.Clean(outputDir) == filepath.Clean(directory) { | ||
| outputDir = "" | ||
| } | ||
|
|
||
| return c.generateMetadata(directory, outputDir) | ||
| } | ||
|
|
||
| // generateMetadata wraps the operator-registry bundle Dockerfile/metadata generator. | ||
| func (c bundleCmd) generateMetadata(manifestsDir, outputDir string) error { | ||
|
estroz marked this conversation as resolved.
|
||
| err := bundle.GenerateFunc(manifestsDir, outputDir, c.operatorName, c.channels, c.defaultChannel, c.overwrite) | ||
| if err != nil { | ||
| return fmt.Errorf("error generating bundle metadata: %v", err) | ||
| } | ||
| return nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,134 @@ | ||||||
| // Copyright 2020 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 bundle | ||||||
|
|
||||||
| import ( | ||||||
| "fmt" | ||||||
|
|
||||||
| log "github.com/sirupsen/logrus" | ||||||
| "github.com/spf13/cobra" | ||||||
| "github.com/spf13/pflag" | ||||||
|
|
||||||
| kbutil "github.com/operator-framework/operator-sdk/internal/util/kubebuilder" | ||||||
| ) | ||||||
|
|
||||||
| //nolint:maligned | ||||||
| type bundleCmd struct { | ||||||
| // Options to turn on different parts of bundling. | ||||||
| kustomize bool | ||||||
| manifests bool | ||||||
| metadata bool | ||||||
|
|
||||||
| // Common options. | ||||||
| operatorName string | ||||||
| version string | ||||||
| inputDir string | ||||||
| outputDir string | ||||||
| manifestRoot string | ||||||
| apisDir string | ||||||
| crdsDir string | ||||||
| stdout bool | ||||||
| quiet bool | ||||||
|
|
||||||
| // Metadata options. | ||||||
| channels string | ||||||
| defaultChannel string | ||||||
| overwrite bool | ||||||
| } | ||||||
|
|
||||||
| //nolint:lll | ||||||
| func NewCmd() *cobra.Command { | ||||||
| c := &bundleCmd{} | ||||||
| cmd := &cobra.Command{ | ||||||
| Use: "bundle", | ||||||
| Short: "Generates bundle data for the operator", | ||||||
| RunE: func(cmd *cobra.Command, args []string) error { | ||||||
| if len(args) != 0 { | ||||||
| return fmt.Errorf("command %s doesn't accept any arguments", cmd.CommandPath()) | ||||||
| } | ||||||
|
|
||||||
| // Generate kustomize bases, manifests, and metadata by default if no | ||||||
| // flags are set so the default behavior is "do everything". | ||||||
| fs := cmd.Flags() | ||||||
| if !fs.Changed("kustomize") && !fs.Changed("metadata") && !fs.Changed("manifests") { | ||||||
| c.kustomize = true | ||||||
| c.manifests = true | ||||||
| c.metadata = true | ||||||
| } | ||||||
|
|
||||||
| cfg, err := kbutil.ReadConfig() | ||||||
| if err != nil { | ||||||
| return fmt.Errorf("error reading configuration: %v", err) | ||||||
| } | ||||||
| c.setCommonDefaults(cfg) | ||||||
|
|
||||||
| if c.kustomize { | ||||||
| if err = c.runKustomize(cfg); err != nil { | ||||||
| log.Fatalf("Error generating bundle bases: %v", err) | ||||||
| } | ||||||
| } | ||||||
| if c.manifests { | ||||||
| if err = c.validateManifests(cfg); err != nil { | ||||||
| return fmt.Errorf("invalid command options: %v", err) | ||||||
| } | ||||||
| if err = c.runManifests(cfg); err != nil { | ||||||
| log.Fatalf("Error generating bundle manifests: %v", err) | ||||||
| } | ||||||
| } | ||||||
| if c.metadata { | ||||||
| if err = c.runMetadata(); err != nil { | ||||||
| log.Fatalf("Error generating bundle metadata: %v", err) | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| return nil | ||||||
| }, | ||||||
| } | ||||||
|
|
||||||
| cmd.Flags().BoolVar(&c.kustomize, "kustomize", false, "Generate kustomize bases") | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you set
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My suggestion is made clear that if we set the option than means that only that will be exec which in POV not so intuitive as described here: https://github.com/operator-framework/operator-sdk/pull/2860/files#r427888547
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you only set |
||||||
| cmd.Flags().BoolVar(&c.manifests, "manifests", false, "Generate bundle manifests") | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto ^ |
||||||
| cmd.Flags().BoolVar(&c.metadata, "metadata", false, "Generate bundle metadata and Dockerfile") | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto ^ |
||||||
| cmd.Flags().BoolVar(&c.stdout, "stdout", false, "Write bundle manifest to stdout") | ||||||
|
|
||||||
| c.addCommonFlagsTo(cmd.Flags()) | ||||||
|
|
||||||
| return cmd | ||||||
| } | ||||||
|
|
||||||
| // TODO(estroz): add flag to skip API metadata regeneration. | ||||||
| func (c *bundleCmd) addCommonFlagsTo(fs *pflag.FlagSet) { | ||||||
| fs.StringVar(&c.operatorName, "operator-name", "", "Name of the bundle's operator") | ||||||
| fs.StringVarP(&c.version, "version", "v", "", "Semantic version of the operator in the generated bundle. "+ | ||||||
| "Only set if creating a new bundle or upgrading your operator") | ||||||
| fs.StringVar(&c.inputDir, "input-dir", "", "Directory to read an existing bundle from. "+ | ||||||
| "This directory is the parent of your bundle 'manifests' directory, and different from --manifest-root") | ||||||
| fs.StringVar(&c.outputDir, "output-dir", "", "Directory to write the bundle to") | ||||||
|
|
||||||
| fs.StringVar(&c.manifestRoot, "manifest-root", "", "Root directory for operator manifests such as "+ | ||||||
| "Deployments and RBAC, ex. 'deploy' or 'config'. This directory is different from that passed to --input-dir") | ||||||
| // NB(estroz): still debating the name of this flag. For now, hide it as an | ||||||
| // "alpha" flag so we do not have to deprecate it if we change this name. | ||||||
| // TODO(estroz): decide on this flag's name before making 'init' default. | ||||||
| if err := fs.MarkHidden("manifest-root"); err != nil { | ||||||
| panic(err) | ||||||
| } | ||||||
|
|
||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we just improve the info to clarifies by giving examples over what from where is expected usually? Eg. Directory to read an existing bundle format from. Eg
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For |
||||||
| fs.StringVar(&c.apisDir, "apis-dir", "", "Root directory for API type defintions") | ||||||
| fs.StringVar(&c.crdsDir, "crds-dir", "", "Root directory for CustomResoureDefinition manifests") | ||||||
| fs.StringVar(&c.channels, "channels", "alpha", "A comma-separated list of channels the bundle belongs to") | ||||||
| fs.StringVar(&c.defaultChannel, "default-channel", "", "The default channel for the bundle") | ||||||
| fs.BoolVar(&c.overwrite, "overwrite", false, "Overwrite the bundle's metadata and Dockerfile if they exist") | ||||||
| fs.BoolVarP(&c.quiet, "quiet", "q", false, "Run in quiet mode") | ||||||
| } | ||||||
Uh oh!
There was an error while loading. Please reload this page.