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
6 changes: 3 additions & 3 deletions cmd/operator-sdk/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/operator-framework/operator-sdk/cmd/operator-sdk/bundle"
"github.com/operator-framework/operator-sdk/cmd/operator-sdk/cleanup"
"github.com/operator-framework/operator-sdk/cmd/operator-sdk/completion"
"github.com/operator-framework/operator-sdk/cmd/operator-sdk/generate"
"github.com/operator-framework/operator-sdk/cmd/operator-sdk/olm"
"github.com/operator-framework/operator-sdk/cmd/operator-sdk/run"
"github.com/operator-framework/operator-sdk/cmd/operator-sdk/version"
Expand All @@ -39,15 +40,14 @@ var commands = []*cobra.Command{
// new.NewCmd()

alpha.NewCmd(),
build.NewCmd(),
bundle.NewCmd(),
cleanup.NewCmd(),
completion.NewCmd(),
generate.NewCmd(),
olm.NewCmd(),
run.NewCmd(),
version.NewCmd(),
build.NewCmd(),

// TODO(hasbro17): add generate csv command after aligning it for kubebuilder layout
}

func Run() error {
Expand Down
2 changes: 1 addition & 1 deletion cmd/operator-sdk/cli/legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func GetCLIRoot() *cobra.Command {
cleanup.NewCmd(),
completion.NewCmd(),
execentrypoint.NewCmd(),
generate.NewCmd(),
generate.NewCmdLegacy(),
migrate.NewCmd(),
new.NewCmd(),
olm.NewCmd(),
Expand Down
209 changes: 209 additions & 0 deletions cmd/operator-sdk/generate/bundle/bundle.go
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 {
Comment thread
estroz marked this conversation as resolved.

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 {
Comment thread
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
}
134 changes: 134 additions & 0 deletions cmd/operator-sdk/generate/bundle/cmd.go
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")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
cmd.Flags().BoolVar(&c.kustomize, "kustomize", false, "Generate kustomize bases")
cmd.Flags().BoolVar(&c.kustomize, "kustomize", false, "If set, generate ONLY the kustomize config bases")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

If you set --kustomize --manifests, both bases and manifests will be generated. I think giving this a simple help description is ok for now.

Copy link
Copy Markdown
Contributor

@camilamacedo86 camilamacedo86 May 22, 2020

Choose a reason for hiding this comment

The 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

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

If you only set --kustomize, only kustomize bases are generated, as you'd expect. If you set another flag with --kustomize like --manifests, then both kustomize bases and manifests will be generated. I think that's what you're trying to say right?

cmd.Flags().BoolVar(&c.manifests, "manifests", false, "Generate bundle manifests")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
cmd.Flags().BoolVar(&c.manifests, "manifests", false, "Generate bundle manifests")
cmd.Flags().BoolVar(&c.manifests, "manifests", false, "If set, generate ONLY the bundle manifests")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The 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")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
cmd.Flags().BoolVar(&c.metadata, "metadata", false, "Generate bundle metadata and Dockerfile")
cmd.Flags().BoolVar(&c.metadata, "metadata", false, "If set, generate ONLY the bundle metadata and its Dockerfile")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The 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)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

For --manifest-root? The help lists deploy and config as example values to pass. I expect we can improve this flag in #2860.

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")
}
Loading