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
13 changes: 10 additions & 3 deletions cmd/operator-sdk/generate/bundle/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func (c bundleCmd) runKustomize(cfg *config.Config) error {
OperatorType: genutil.PluginKeyToOperatorType(cfg.Layout),
}
opts := []gencsv.Option{
gencsv.WithBase(c.inputDir, c.apisDir),
gencsv.WithBase(c.inputDir, c.apisDir, c.interactiveLevel),
gencsv.WithBaseWriter(c.outputDir),
}
if err := csvGen.Generate(cfg, opts...); err != nil {
Expand Down Expand Up @@ -152,7 +152,7 @@ func (c bundleCmd) runManifests(cfg *config.Config) (err error) {

stdout := genutil.NewMultiManifestWriter(os.Stdout)
opts := []gencsv.Option{
gencsv.WithBase(c.inputDir, c.apisDir),
gencsv.WithBase(c.inputDir, c.apisDir, c.interactiveLevel),
}
if c.stdout {
opts = append(opts, gencsv.WithWriter(stdout))
Expand Down Expand Up @@ -187,7 +187,14 @@ func (c bundleCmd) runMetadata() error {

directory := c.inputDir
if directory == "" {
directory = filepath.Join("config", "bundle", bundle.ManifestsDir)
// There may be no existing bundle at the default path, so assume manifests
// only exist in the output directory.
defaultDirectory := filepath.Join("config", "bundle", 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)
}
Expand Down
18 changes: 18 additions & 0 deletions cmd/operator-sdk/generate/bundle/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/spf13/pflag"

kbutil "github.com/operator-framework/operator-sdk/internal/util/kubebuilder"
"github.com/operator-framework/operator-sdk/internal/util/projutil"
)

//nolint:maligned
Expand All @@ -42,6 +43,10 @@ type bundleCmd struct {
stdout bool
quiet bool

// Interactive options.
interactiveLevel projutil.InteractiveLevel
interactive bool

// Metadata options.
channels string
defaultChannel string
Expand All @@ -59,6 +64,17 @@ func NewCmd() *cobra.Command {
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 kustomize bases, manifests, and metadata by default if no
// flags are set so the default behavior is "do everything".
fs := cmd.Flags()
Expand Down Expand Up @@ -131,4 +147,6 @@ func (c *bundleCmd) addCommonFlagsTo(fs *pflag.FlagSet) {
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")
fs.BoolVar(&c.interactive, "interactive", false, "When set or no bundle base exists, an interactive "+
"command prompt will be presented to accept bundle ClusterServiceVersion metadata")
}
10 changes: 10 additions & 0 deletions cmd/operator-sdk/generate/internal/genutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package genutil

import (
"bytes"
"errors"
"fmt"
"io"
"os"
Expand Down Expand Up @@ -126,3 +127,12 @@ func (w *multiManifestWriter) Write(b []byte) (int, error) {
func NewMultiManifestWriter(w io.Writer) io.Writer {
return &multiManifestWriter{w}
}

// IsNotExist returns true if path does not exist on disk.
func IsNotExist(path string) bool {
if path == "" {
return true
}
_, err := os.Stat(path)
return err != nil && errors.Is(err, os.ErrNotExist)
}
17 changes: 12 additions & 5 deletions internal/generate/clusterserviceversion/clusterserviceversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ type getBaseFunc func() (*operatorsv1alpha1.ClusterServiceVersion, error)
type Option func(*Generator) error

// WithBase sets a Generator's base CSV to a kustomize-style base.
func WithBase(inputDir, apisDir string) Option {
func WithBase(inputDir, apisDir string, ilvl projutil.InteractiveLevel) Option {
return func(g *Generator) error {
g.getBase = g.makeKustomizeBaseGetter(inputDir, apisDir)
g.getBase = g.makeKustomizeBaseGetter(inputDir, apisDir, ilvl)
return nil
}
}
Expand Down Expand Up @@ -179,18 +179,18 @@ func makeCSVFileName(name string) string {
}

// makeKustomizeBaseGetter returns a function that gets a kustomize-style base.
func (g Generator) makeKustomizeBaseGetter(inputDir, apisDir string) getBaseFunc {
func (g Generator) makeKustomizeBaseGetter(inputDir, apisDir string, ilvl projutil.InteractiveLevel) getBaseFunc {
basePath := filepath.Join(inputDir, "bases", makeCSVFileName(g.OperatorName))
if genutil.IsNotExist(basePath) {
basePath = ""
}

return g.makeBaseGetter(basePath, apisDir)
return g.makeBaseGetter(basePath, apisDir, requiresInteraction(basePath, ilvl))
}

// makeBaseGetter returns a function that gets a base from inputDir.
// apisDir is used by getBaseFunc to populate base fields.
func (g Generator) makeBaseGetter(basePath, apisDir string) getBaseFunc {
func (g Generator) makeBaseGetter(basePath, apisDir string, interactive bool) getBaseFunc {
gvks := make([]schema.GroupVersionKind, len(g.config.Resources))
for i, gvk := range g.config.Resources {
gvks[i].Group = fmt.Sprintf("%s.%s", gvk.Group, g.config.Domain)
Expand All @@ -205,11 +205,18 @@ func (g Generator) makeBaseGetter(basePath, apisDir string) getBaseFunc {
BasePath: basePath,
APIsDir: apisDir,
GVKs: gvks,
Interactive: interactive,
}
return b.GetBase()
}
}

// requiresInteraction checks if the combination of ilvl and basePath existence
// requires the generator prompt a user interactively.
func requiresInteraction(basePath string, ilvl projutil.InteractiveLevel) bool {
return (ilvl == projutil.InteractiveSoftOff && genutil.IsNotExist(basePath)) || ilvl == projutil.InteractiveOnAll
}

// updateVersions updates csv's version and data involving the version,
// ex. ObjectMeta.Name, and place the old version in the `replaces` object,
// if there is an old version to replace.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ var _ = Describe("Generating a ClusterServiceVersion", func() {
Collector: col,
}
opts := []Option{
WithBase(csvBasesDir, goAPIsDir),
WithBase(csvBasesDir, goAPIsDir, projutil.InteractiveHardOff),
WithWriter(buf),
}
Expect(g.Generate(cfg, opts...)).ToNot(HaveOccurred())
Expand All @@ -129,11 +129,11 @@ var _ = Describe("Generating a ClusterServiceVersion", func() {
OperatorType: operatorType,
}
opts := []Option{
WithBase(csvBasesDir, goAPIsDir),
WithBase(csvBasesDir, goAPIsDir, projutil.InteractiveHardOff),
WithBaseWriter(tmp),
}
Expect(g.Generate(cfg, opts...)).ToNot(HaveOccurred())
outputFile := filepath.Join(tmp, "bases", makeCSVFileName("memcached-operator"))
outputFile := filepath.Join(tmp, "bases", makeCSVFileName(operatorName))
Expect(outputFile).To(BeAnExistingFile())
Expect(string(readFileHelper(outputFile))).To(MatchYAML(baseCSVUIMetaStr))
})
Expand All @@ -145,11 +145,11 @@ var _ = Describe("Generating a ClusterServiceVersion", func() {
Collector: col,
}
opts := []Option{
WithBase(csvBasesDir, goAPIsDir),
WithBase(csvBasesDir, goAPIsDir, projutil.InteractiveHardOff),
WithBundleWriter(tmp),
}
Expect(g.Generate(cfg, opts...)).ToNot(HaveOccurred())
outputFile := filepath.Join(tmp, bundle.ManifestsDir, makeCSVFileName("memcached-operator"))
outputFile := filepath.Join(tmp, bundle.ManifestsDir, makeCSVFileName(operatorName))
Expect(outputFile).To(BeAnExistingFile())
Expect(string(readFileHelper(outputFile))).To(MatchYAML(newCSVStr))
})
Expand All @@ -172,7 +172,7 @@ var _ = Describe("Generating a ClusterServiceVersion", func() {
})
It("should return an error without a getWriter", func() {
opts := []Option{
WithBase(csvBasesDir, goAPIsDir),
WithBase(csvBasesDir, goAPIsDir, projutil.InteractiveHardOff),
}
Expect(g.Generate(cfg, opts...)).To(MatchError(noGetWriterError))
})
Expand Down Expand Up @@ -282,6 +282,43 @@ var _ = Describe("Generating a ClusterServiceVersion", func() {

})

var _ = Describe("Generation requires interaction", func() {
var (
testExistingPath = filepath.Join(csvBasesDir, "memcached-operator.clusterserviceversion.yaml")
testNotExistingPath = filepath.Join(csvBasesDir, "notexist.clusterserviceversion.yaml")
)

Context("when base path does not exist", func() {
By("turning interaction off explicitly")
It("returns false", func() {
Expect(requiresInteraction(testNotExistingPath, projutil.InteractiveHardOff)).To(BeFalse())
})
By("turning interaction off implicitly")
It("returns true", func() {
Expect(requiresInteraction(testNotExistingPath, projutil.InteractiveSoftOff)).To(BeTrue())
})
By("turning interaction on explicitly")
It("returns true", func() {
Expect(requiresInteraction(testNotExistingPath, projutil.InteractiveOnAll)).To(BeTrue())
})
})

Context("when base path does exist", func() {
By("turning interaction off explicitly")
It("returns false", func() {
Expect(requiresInteraction(testExistingPath, projutil.InteractiveHardOff)).To(BeFalse())
})
By("turning interaction off implicitly")
It("returns false", func() {
Expect(requiresInteraction(testExistingPath, projutil.InteractiveSoftOff)).To(BeFalse())
})
By("turning interaction on explicitly")
It("returns true", func() {
Expect(requiresInteraction(testExistingPath, projutil.InteractiveOnAll)).To(BeTrue())
})
})
})

func readConfigHelper(dir string) *config.Config {
wd, err := os.Getwd()
ExpectWithOffset(1, err).ToNot(HaveOccurred())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,6 @@ func mutatingToWebhookDescription(webhook admissionregv1.MutatingWebhook) operat
func applyCustomResources(c *collector.Manifests, csv *operatorsv1alpha1.ClusterServiceVersion) error {
examples := []json.RawMessage{}
for _, cr := range c.CustomResources {
fmt.Println("applying", cr.GetName())
crBytes, err := cr.MarshalJSON()
if err != nil {
return err
Expand All @@ -228,7 +227,6 @@ func applyCustomResources(c *collector.Manifests, csv *operatorsv1alpha1.Cluster
csv.SetAnnotations(make(map[string]string))
}
csv.GetAnnotations()["alm-examples"] = string(examplesJSON)
fmt.Println("applied")

return nil
}
Expand Down