From de60ef8210d6c56147aefc492543237022901795 Mon Sep 17 00:00:00 2001 From: Catherine Chan-Tse Date: Tue, 27 Sep 2022 19:04:20 -0400 Subject: [PATCH] Add pipe support to render-veneer basic command Signed-off-by: Catherine Chan-Tse --- alpha/declcfg/load.go | 6 ++++-- alpha/declcfg/load_test.go | 4 ++-- alpha/veneer/basic/basic.go | 39 +++++++--------------------------- cmd/opm/alpha/veneer/basic.go | 21 +++++++++++++----- cmd/opm/alpha/veneer/cmd.go | 11 ++++++++++ cmd/opm/alpha/veneer/semver.go | 28 +++++++----------------- 6 files changed, 49 insertions(+), 60 deletions(-) diff --git a/alpha/declcfg/load.go b/alpha/declcfg/load.go index 0729c751a..65c289780 100644 --- a/alpha/declcfg/load.go +++ b/alpha/declcfg/load.go @@ -118,7 +118,9 @@ func extractCSV(objs []string) string { return "" } -func readYAMLOrJSON(r io.Reader) (*DeclarativeConfig, error) { +// LoadReader reads yaml or json from the passed in io.Reader and unmarshals it into a DeclarativeConfig struct. +// Path references will not be de-referenced so callers are responsible for de-referencing if necessary. +func LoadReader(r io.Reader) (*DeclarativeConfig, error) { cfg := &DeclarativeConfig{} dec := yaml.NewYAMLOrJSONDecoder(r, 4096) for { @@ -173,7 +175,7 @@ func LoadFile(root fs.FS, path string) (*DeclarativeConfig, error) { } defer file.Close() - cfg, err := readYAMLOrJSON(file) + cfg, err := LoadReader(file) if err != nil { return nil, err } diff --git a/alpha/declcfg/load_test.go b/alpha/declcfg/load_test.go index eb2ed8d49..301002f16 100644 --- a/alpha/declcfg/load_test.go +++ b/alpha/declcfg/load_test.go @@ -14,7 +14,7 @@ import ( "github.com/operator-framework/operator-registry/alpha/property" ) -func TestReadYAMLOrJSON(t *testing.T) { +func TestLoadReader(t *testing.T) { type spec struct { name string fsys fs.FS @@ -80,7 +80,7 @@ func TestReadYAMLOrJSON(t *testing.T) { f, err := s.fsys.Open(s.path) require.NoError(t, err) - cfg, err := readYAMLOrJSON(f) + cfg, err := LoadReader(f) s.assertion(t, err) if err == nil { require.NotNil(t, cfg) diff --git a/alpha/veneer/basic/basic.go b/alpha/veneer/basic/basic.go index 1986608d3..26c2788b5 100644 --- a/alpha/veneer/basic/basic.go +++ b/alpha/veneer/basic/basic.go @@ -2,9 +2,8 @@ package basic import ( "context" - "errors" - "os" - "path/filepath" + "fmt" + "io" "github.com/operator-framework/operator-registry/alpha/action" "github.com/operator-framework/operator-registry/alpha/declcfg" @@ -15,30 +14,8 @@ type Veneer struct { Registry image.Registry } -func (v Veneer) Render(ctx context.Context, ref string) (*declcfg.DeclarativeConfig, error) { - // only taking first argument as file - stat, serr := os.Stat(ref) - if serr != nil { - return nil, serr - } - - if stat.IsDir() { - return nil, errors.New("cannot render veneers by directory reference") - } - return v.renderFile(ctx, ref) -} - -func (v Veneer) renderFile(ctx context.Context, ref string) (*declcfg.DeclarativeConfig, error) { - // xform any relative to absolute paths - abspath, err := filepath.Abs(ref) - if err != nil { - return nil, err - } - // xform to break apart dir/file elements - rpath, fname := filepath.Split(abspath) - root := os.DirFS(rpath) - - cfg, err := declcfg.LoadFile(root, fname) +func (v Veneer) Render(ctx context.Context, reader io.Reader) (*declcfg.DeclarativeConfig, error) { + cfg, err := declcfg.LoadReader(reader) if err != nil { return cfg, err } @@ -52,8 +29,7 @@ func (v Veneer) renderFile(ctx context.Context, ref string) (*declcfg.Declarativ for _, b := range cfg.Bundles { if !isBundleVeneer(&b) { - outb = append(outb, b) - continue + return nil, fmt.Errorf("unexpected fields present in basic veneer bundle") } r.Refs = []string{b.Image} contributor, err := r.Run(ctx) @@ -67,7 +43,8 @@ func (v Veneer) renderFile(ctx context.Context, ref string) (*declcfg.Declarativ return cfg, nil } -// isBundleVeneer identifies loaded partial Bundle data from YAML/JSON veneer source as having no properties, +// isBundleVeneer identifies a Bundle veneer source as having a Schema and Image defined +// but no Properties, RelatedImages or Package defined func isBundleVeneer(b *declcfg.Bundle) bool { - return len(b.Properties) == 0 + return b.Schema != "" && b.Image != "" && b.Package == "" && len(b.Properties) == 0 && len(b.RelatedImages) == 0 } diff --git a/cmd/opm/alpha/veneer/basic.go b/cmd/opm/alpha/veneer/basic.go index 7ff2c9613..40311c0eb 100644 --- a/cmd/opm/alpha/veneer/basic.go +++ b/cmd/opm/alpha/veneer/basic.go @@ -20,11 +20,22 @@ func newBasicVeneerRenderCmd() *cobra.Command { output string ) cmd := &cobra.Command{ - Use: "basic basic-veneer-file", - Short: "Generate a declarative config blob from a single 'basic veneer' file", - Long: `Generate a declarative config blob from a single 'basic veneer' file, typified as a declarative configuration file where olm.bundle objects have no properties`, - Args: cobra.ExactArgs(1), + Use: "basic basic-veneer-file", + Short: `Generate a file-based catalog from a single 'basic veneer' file +When FILE is '-' or not provided, the veneer is read from standard input`, + Long: `Generate a file-based catalog from a single 'basic veneer' file +When FILE is '-' or not provided, the veneer is read from standard input`, + Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { + // Handle different input argument types + // When no arguments or "-" is passed to the command, + // assume input is coming from stdin + // Otherwise open the file passed to the command + data, source, err := openFileOrStdin(cmd, args) + if err != nil { + log.Fatalf("unable to open %q: %v", source, err) + } + defer data.Close() var write func(declcfg.DeclarativeConfig, io.Writer) error switch output { @@ -50,7 +61,7 @@ func newBasicVeneerRenderCmd() *cobra.Command { veneer.Registry = reg // only taking first file argument - cfg, err := veneer.Render(cmd.Context(), args[0]) + cfg, err := veneer.Render(cmd.Context(), data) if err != nil { log.Fatal(err) } diff --git a/cmd/opm/alpha/veneer/cmd.go b/cmd/opm/alpha/veneer/cmd.go index 63ab0823b..2bdec4e1f 100644 --- a/cmd/opm/alpha/veneer/cmd.go +++ b/cmd/opm/alpha/veneer/cmd.go @@ -1,6 +1,9 @@ package veneer import ( + "io" + "os" + "github.com/spf13/cobra" ) @@ -16,3 +19,11 @@ func NewCmd() *cobra.Command { return runCmd } + +func openFileOrStdin(cmd *cobra.Command, args []string) (io.ReadCloser, string, error) { + if len(args) == 0 || args[0] == "-" { + return io.NopCloser(cmd.InOrStdin()), "stdin", nil + } + reader, err := os.Open(args[0]) + return reader, args[0], err +} diff --git a/cmd/opm/alpha/veneer/semver.go b/cmd/opm/alpha/veneer/semver.go index 42127dd5c..0dd4bd122 100644 --- a/cmd/opm/alpha/veneer/semver.go +++ b/cmd/opm/alpha/veneer/semver.go @@ -18,19 +18,22 @@ import ( func newSemverCmd() *cobra.Command { output := "" cmd := &cobra.Command{ - Use: "semver [FILE]", - Short: "Generate a file-based catalog from a single 'semver veneer' file \nWhen FILE is '-' or not provided, the veneer is read from standard input", - Long: "Generate a file-based catalog from a single 'semver veneer' file \nWhen FILE is '-' or not provided, the veneer is read from standard input", - Args: cobra.MaximumNArgs(1), + Use: "semver [FILE]", + Short: `Generate a file-based catalog from a single 'semver veneer' file +When FILE is '-' or not provided, the veneer is read from standard input`, + Long: `Generate a file-based catalog from a single 'semver veneer' file +When FILE is '-' or not provided, the veneer is read from standard input`, + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { // Handle different input argument types // When no arguments or "-" is passed to the command, // assume input is coming from stdin // Otherwise open the file passed to the command - data, source, err := openFileOrReadStdin(cmd, args) + data, source, err := openFileOrStdin(cmd, args) if err != nil { return err } + defer data.Close() var write func(declcfg.DeclarativeConfig, io.Writer) error switch output { @@ -80,18 +83,3 @@ func newSemverCmd() *cobra.Command { cmd.Flags().StringVarP(&output, "output", "o", "json", "Output format (json|yaml|mermaid)") return cmd } - -func openFileOrReadStdin(cmd *cobra.Command, args []string) (io.Reader, string, error) { - var err error = nil - var source string = "" - var reader io.Reader - - if len(args) == 0 || args[0] == "-" { - reader = cmd.InOrStdin() - source = "stdin" - } else { - reader, err = os.Open(args[0]) - source = args[0] - } - return reader, source, err -}