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: 6 additions & 0 deletions changelog/fragments/3088-addition.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
entries:
- description: >
'bundle generate' generates bundles for current project layouts; this
has the same behavior as 'generate csv --make-manifests=true'

kind: addition
131 changes: 131 additions & 0 deletions cmd/operator-sdk/generate/bundle/bundle_legacy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright 2020 The Operator-SDK Authors
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.

Why do we need to create the legacy one? Is not it == the new one and just change the paths?
Is not harder to have the same implementation duplicated instead of having the code centralized to keep it maintained? WDYT? See: #2948 (comment)

PS.; My first approach was to do it as you did here, however, after some comments from @joelanford I think it makes more sense just change the path instead of it as he suggested.

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.

I'm of the opinion that some code duplication is OK, especially with complex command setup like this. It's easier to maintain, then remove when we remove current project layout support.

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.

I'm okay with duplication in this case as well. From my experience with trying to reuse the code for generate csv to be used by both the old and new CLI commands it made the underlying command a lot more convoluted.

@camilamacedo86 operator-sdk run local was a much simpler command where we just had to change 1 path.

//
// 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"
"path/filepath"

"github.com/operator-framework/operator-registry/pkg/lib/bundle"
log "github.com/sirupsen/logrus"

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"
"github.com/operator-framework/operator-sdk/internal/util/projutil"
)

// setCommonDefaultsLegacy sets defaults useful to all modes of this subcommand.
func (c *bundleCmd) setCommonDefaultsLegacy() {
Comment thread
estroz marked this conversation as resolved.
if c.operatorName == "" {
c.operatorName = filepath.Base(projutil.MustGetwd())
}
}

// validateManifestsLegacy validates c for bundle manifests generation for
// legacy project layouts.
func (c bundleCmd) validateManifestsLegacy() error {
if c.version != "" {
if err := genutil.ValidateVersion(c.version); err != nil {
return err
}
}
return nil
}

// runManifestsLegacy generates bundle manifests for legacy project layouts.
func (c bundleCmd) runManifestsLegacy() (err error) {

if !c.quiet {
if c.version == "" {
log.Info("Generating bundle manifests")
} else {
log.Info("Generating bundle manifests version", c.version)
}
}

if c.apisDir == "" {
c.apisDir = filepath.Join("pkg", "apis")
}
if c.manifestRoot == "" {
c.manifestRoot = "deploy"
}
if c.crdsDir == "" {
c.crdsDir = filepath.Join(c.manifestRoot, "crds")
}
defaultBundleDir := filepath.Join(c.manifestRoot, "olm-catalog", c.operatorName)
if c.inputDir == "" {
c.inputDir = defaultBundleDir
}
if c.outputDir == "" {
c.outputDir = defaultBundleDir
}

col := &collector.Manifests{}
if err := col.UpdateFromDirs(c.manifestRoot, c.crdsDir); err != nil {
return err
}

csvGen := gencsv.Generator{
OperatorName: c.operatorName,
OperatorType: projutil.GetOperatorType(),
Version: c.version,
Collector: col,
}

opts := []gencsv.LegacyOption{
gencsv.WithBundleBase(c.inputDir, c.apisDir, c.interactiveLevel),
gencsv.LegacyOption(gencsv.WithBundleWriter(c.outputDir)),
}
if err := csvGen.GenerateLegacy(opts...); err != nil {
return fmt.Errorf("error generating ClusterServiceVersion: %v", err)
}

dir := filepath.Join(c.outputDir, bundle.ManifestsDir)
if err := genutil.WriteCRDFilesLegacy(dir, col.CustomResourceDefinitions...); err != nil {
return err
}

if !c.quiet {
log.Infoln("Bundle manifests generated successfully in", c.outputDir)
}

return nil
}

// runMetadataLegacy generates a bundle.Dockerfile and bundle metadata for
// legacy project layouts.
func (c bundleCmd) runMetadataLegacy() error {

directory := c.inputDir
if directory == "" {
// There may be no existing bundle at the default path, so assume manifests
// were generated in the output directs.
defaultDirectory := filepath.Join("deploy", "olm-catalog", c.operatorName, bundle.ManifestsDir)
if c.outputDir != "" && genutil.IsNotExist(defaultDirectory) {
directory = filepath.Join(c.outputDir, bundle.ManifestsDir)
} else {
directory = defaultDirectory
}
} else {
directory = filepath.Join(directory, bundle.ManifestsDir)
}
outputDir := c.outputDir
if filepath.Clean(outputDir) == filepath.Clean(directory) {
outputDir = ""
}

return c.generateMetadata(directory, outputDir)
}
60 changes: 59 additions & 1 deletion cmd/operator-sdk/generate/bundle/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ type bundleCmd struct {
overwrite bool
}

//nolint:lll
// NewCmd returns the 'bundle' command configured for the new project layout.
func NewCmd() *cobra.Command {
c := &bundleCmd{}
cmd := &cobra.Command{
Expand Down Expand Up @@ -123,6 +123,64 @@ func NewCmd() *cobra.Command {
return cmd
}

// NewCmdLegacy returns the 'bundle' command configured for the legacy project layout.
func NewCmdLegacy() *cobra.Command {
Comment thread
estroz marked this conversation as resolved.
c := &bundleCmd{}
cmd := &cobra.Command{
Use: "bundle",
Short: "Generates bundle data for the operator",
RunE: func(cmd *cobra.Command, args []string) (err error) {
if len(args) != 0 {
return fmt.Errorf("command %s doesn't accept any arguments", cmd.CommandPath())
}

// Check if the user has any specific preference to enable/disable
// interactive prompts. Default behaviour is to disable the prompt
// unless a base bundle does not exist.
if cmd.Flags().Changed("interactive") {
if c.interactive {
c.interactiveLevel = projutil.InteractiveOnAll
} else {
c.interactiveLevel = projutil.InteractiveHardOff
}
}

// Generate manifests and metadata by default if no flags are set so
// the default behavior is "do everything".
fs := cmd.Flags()
if !fs.Changed("metadata") && !fs.Changed("manifests") {
c.metadata = true
c.manifests = true
}

c.setCommonDefaultsLegacy()

if c.manifests {
if err = c.validateManifestsLegacy(); err != nil {
return fmt.Errorf("invalid command options: %v", err)
}
if err = c.runManifestsLegacy(); err != nil {
log.Fatalf("Error generating bundle manifests: %v", err)
}
}
if c.metadata {
if err = c.runMetadataLegacy(); err != nil {
log.Fatalf("Error generating bundle metadata: %v", err)
}
}

return nil
},
}

cmd.Flags().BoolVar(&c.manifests, "manifests", false, "Generate bundle manifests")
cmd.Flags().BoolVar(&c.metadata, "metadata", false, "Generate bundle metadata and Dockerfile")

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")
Expand Down
3 changes: 2 additions & 1 deletion cmd/operator-sdk/generate/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ code or manifests.`,
}
}

// NewCmdLegacy returns the 'generate' command configured for the new project layout.
// NewCmd returns the 'generate' command configured for the new project layout.
func NewCmd() *cobra.Command {
cmd := newCmd()
cmd.AddCommand(
Expand All @@ -45,6 +45,7 @@ func NewCmdLegacy() *cobra.Command {
newGenerateK8SCmd(),
newGenerateCRDsCmd(),
newGenerateCSVCmd(),
bundle.NewCmdLegacy(),
)
return cmd
}
30 changes: 25 additions & 5 deletions cmd/operator-sdk/generate/internal/genutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,37 @@ func WriteCRDFiles(dir string, crds ...v1beta1.CustomResourceDefinition) error {
return err
}
for _, crd := range crds {
if err := writeCRDFile(dir, crd); err != nil {
if err := writeCRDFile(dir, crd, makeCRDFileName(crd)); err != nil {
return err
}
}
return nil
}

// writeCRDFile marshals crd to bytes and writes them to dir in a file named
// <full group>_<resource>.yaml.
func writeCRDFile(dir string, crd v1beta1.CustomResourceDefinition) error {
file := fmt.Sprintf("%s_%s.yaml", crd.Spec.Group, crd.Spec.Names.Plural)
func makeCRDFileName(crd v1beta1.CustomResourceDefinition) string {
return fmt.Sprintf("%s_%s.yaml", crd.Spec.Group, crd.Spec.Names.Plural)
}

// WriteCRDFilesLegacy creates dir then writes each CustomResourceDefinition
// in crds to a file in legacy format in dir.
func WriteCRDFilesLegacy(dir string, crds ...v1beta1.CustomResourceDefinition) error {
if err := os.MkdirAll(dir, 0700); err != nil {
return err
}
for _, crd := range crds {
if err := writeCRDFile(dir, crd, makeCRDFileNameLegacy(crd)); err != nil {
return err
}
}
return nil
}

func makeCRDFileNameLegacy(crd v1beta1.CustomResourceDefinition) string {
return fmt.Sprintf("%s_%s_crd.yaml", crd.Spec.Group, crd.Spec.Names.Plural)
}

// writeCRDFile marshals crd to bytes and writes them to dir in file.
func writeCRDFile(dir string, crd v1beta1.CustomResourceDefinition, file string) error {
f, err := os.Create(filepath.Join(dir, file))
if err != nil {
return err
Expand Down
44 changes: 42 additions & 2 deletions hack/tests/subcommand-generate-csv.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,11 @@ function check_crd_files() {
}

function generate_csv() {
echo "operator-sdk generate csv --operator-name $OPERATOR_NAME --interactive=false $@"
operator-sdk generate csv --operator-name $OPERATOR_NAME --interactive=false $@
echo_run operator-sdk generate csv --operator-name $OPERATOR_NAME --interactive=false $@
}

function generate_bundle() {
echo_run operator-sdk generate bundle --operator-name $OPERATOR_NAME --interactive=false $@
}

pushd "$TEST_DIR" > /dev/null
Expand Down Expand Up @@ -96,3 +99,40 @@ check_crd_files "$TEST_NAME" "$OUTPUT_DIR/manifests" 1
cleanup_case

header_text "All 'operator-sdk generate csv' subcommand tests passed."

header_text "Running 'operator-sdk generate bundle' subcommand tests in $TEST_DIR."

TEST_NAME="generate with version $OPERATOR_VERSION"
header_text "$TEST_NAME"
generate_bundle --version $OPERATOR_VERSION
check_dir "$TEST_NAME" "$DEFAULT_BUNDLE_DIR" 0
check_dir "$TEST_NAME" "$OPERATOR_BUNDLE_ROOT_DIR/manifests" 1
check_dir "$TEST_NAME" "$OPERATOR_BUNDLE_ROOT_DIR/metadata" 1
check_csv_file "$TEST_NAME" "$OPERATOR_BUNDLE_ROOT_DIR/manifests" 1
check_crd_files "$TEST_NAME" "$OPERATOR_BUNDLE_ROOT_DIR/manifests" 1
check_file "$TEST_NAME" "bundle.Dockerfile" 1
cleanup_case

TEST_NAME="generate manifests only with version $OPERATOR_VERSION"
header_text "$TEST_NAME"
generate_bundle --version $OPERATOR_VERSION --manifests
check_dir "$TEST_NAME" "$DEFAULT_BUNDLE_DIR" 0
check_dir "$TEST_NAME" "$OPERATOR_BUNDLE_ROOT_DIR/manifests" 1
check_dir "$TEST_NAME" "$OPERATOR_BUNDLE_ROOT_DIR/metadata" 0
check_csv_file "$TEST_NAME" "$OPERATOR_BUNDLE_ROOT_DIR/manifests" 1
check_crd_files "$TEST_NAME" "$OPERATOR_BUNDLE_ROOT_DIR/manifests" 1
check_file "$TEST_NAME" "bundle.Dockerfile" 0
cleanup_case

TEST_NAME="generate with version $OPERATOR_VERSION and output-dir"
header_text "$TEST_NAME"
generate_bundle --version $OPERATOR_VERSION --output-dir "$OUTPUT_DIR"
check_dir "$TEST_NAME" "$OPERATOR_BUNDLE_ROOT_DIR/manifests" 0
check_dir "$TEST_NAME" "$OUTPUT_DIR/manifests" 1
check_dir "$TEST_NAME" "$OUTPUT_DIR/metadata" 1
check_csv_file "$TEST_NAME" "$OUTPUT_DIR/manifests" 1
check_crd_files "$TEST_NAME" "$OUTPUT_DIR/manifests" 1
check_file "$TEST_NAME" "bundle.Dockerfile" 1
cleanup_case

header_text "All 'operator-sdk generate bundle' subcommand tests passed."
Loading