diff --git a/alpha/template/basic/basic.go b/alpha/template/basic/basic.go index 18566fbcf2..efc5179917 100644 --- a/alpha/template/basic/basic.go +++ b/alpha/template/basic/basic.go @@ -4,22 +4,42 @@ import ( "context" "fmt" "io" + "slices" "github.com/operator-framework/operator-registry/alpha/action" "github.com/operator-framework/operator-registry/alpha/declcfg" "github.com/operator-framework/operator-registry/pkg/image" ) +const schema string = "olm.template.basic" + type Template struct { Registry image.Registry } func (t Template) Render(ctx context.Context, reader io.Reader) (*declcfg.DeclarativeConfig, error) { + cfg, err := declcfg.LoadReader(reader) if err != nil { return cfg, err } + // declcfg.LoadReader interprets/validates anything behind an fs.FS to a declcfg.DeclarativeConfig, + // including any other declcfg.Meta objects (in declcfg.DeclarativeConfig.Others) that may be present, + // including our mandatory schema, which we can now validate and remove from the Others slice. + validatedSchema := false + cfg.Others = slices.DeleteFunc(cfg.Others, func(i declcfg.Meta) bool { + if i.Schema == schema { + validatedSchema = true + return true + } + return false + }) + + if !validatedSchema { + return nil, fmt.Errorf("missing/invalid schema. basic template schema must be %q", schema) + } + outb := cfg.Bundles[:0] // allocate based on max size of input, but empty slice // populate registry, incl any flags from CLI, and enforce only rendering bundle images r := action.Render{ diff --git a/alpha/template/composite/builder_test.go b/alpha/template/composite/builder_test.go index 4a70c30670..ec320738e9 100644 --- a/alpha/template/composite/builder_test.go +++ b/alpha/template/composite/builder_test.go @@ -237,7 +237,7 @@ func TestBasicBuilder(t *testing.T) { defer file.Close() fileData, err := io.ReadAll(file) require.NoError(t, err) - require.Equal(t, string(fileData), basicBuiltFbcYaml) + require.Equal(t, basicBuiltFbcYaml, string(fileData)) }, validateAssertions: func(t *testing.T, validateErr error) { require.NoError(t, validateErr) @@ -283,6 +283,8 @@ func TestBasicBuilder(t *testing.T) { } const basicYaml = `--- +schema: olm.template.basic +--- defaultChannel: preview name: webhook-operator schema: olm.package diff --git a/alpha/template/template.go b/alpha/template/template.go new file mode 100644 index 0000000000..e66f7930d1 --- /dev/null +++ b/alpha/template/template.go @@ -0,0 +1,7 @@ +package template + +type Template interface { + Identify() []string + Render() error +} + diff --git a/cmd/opm/alpha/template/basic.go b/cmd/opm/alpha/template/basic.go index 5d34ec2cd8..ab013fffbb 100644 --- a/cmd/opm/alpha/template/basic.go +++ b/cmd/opm/alpha/template/basic.go @@ -16,7 +16,6 @@ import ( func newBasicTemplateCmd() *cobra.Command { var ( template basic.Template - output string ) cmd := &cobra.Command{ Use: "basic basic-template-file", @@ -37,6 +36,10 @@ When FILE is '-' or not provided, the template is read from standard input`, defer data.Close() var write func(declcfg.DeclarativeConfig, io.Writer) error + output, err := cmd.Flags().GetString("output") + if err != nil { + log.Fatalf("unable to determine output format") + } switch output { case "yaml": write = declcfg.WriteYAML @@ -70,6 +73,5 @@ When FILE is '-' or not provided, the template is read from standard input`, } }, } - cmd.Flags().StringVarP(&output, "output", "o", "json", "Output format (json|yaml)") return cmd } diff --git a/cmd/opm/alpha/template/cmd.go b/cmd/opm/alpha/template/cmd.go index 1c435e6fa2..74fcda1ba1 100644 --- a/cmd/opm/alpha/template/cmd.go +++ b/cmd/opm/alpha/template/cmd.go @@ -8,15 +8,27 @@ import ( ) func NewCmd() *cobra.Command { + var output string + runCmd := &cobra.Command{ Use: "render-template", Short: "Render a catalog template type", Args: cobra.NoArgs, } - runCmd.AddCommand(newBasicTemplateCmd()) - runCmd.AddCommand(newSemverTemplateCmd()) - runCmd.AddCommand(newCompositeTemplateCmd()) + bc := newBasicTemplateCmd() + // bc.Hidden = true + runCmd.AddCommand(bc) + + sc := newSemverTemplateCmd() + // sc.Hidden = true + runCmd.AddCommand(sc) + + cc := newCompositeTemplateCmd() + // cc.Hidden = true + runCmd.AddCommand(cc) + + runCmd.PersistentFlags().StringVarP(&output, "output", "o", "json", "Output format (json|yaml)") return runCmd } diff --git a/cmd/opm/alpha/template/composite.go b/cmd/opm/alpha/template/composite.go index 20c9a1e5c1..fe364131ac 100644 --- a/cmd/opm/alpha/template/composite.go +++ b/cmd/opm/alpha/template/composite.go @@ -14,13 +14,11 @@ import ( func newCompositeTemplateCmd() *cobra.Command { var ( - output string - validate bool - compositeFile string - catalogFile string + validate bool + catalogFile string ) cmd := &cobra.Command{ - Use: "composite", + Use: "composite [FILE]", Short: `Generate file-based catalogs from a catalog configuration file and a 'composite template' file`, Long: `Generate file-based catalogs from a catalog configuration file @@ -28,6 +26,10 @@ and a 'composite template' file`, Args: cobra.MaximumNArgs(0), Run: func(cmd *cobra.Command, args []string) { + output, err := cmd.Flags().GetString("output") + if err != nil { + log.Fatalf("unable to determine output format") + } switch output { case "yaml": // do nothing @@ -43,18 +45,6 @@ and a 'composite template' file`, } defer reg.Destroy() - // operator author's 'composite.yaml' file - compositeReader, err := os.Open(compositeFile) - if err != nil { - log.Fatalf("opening composite config file %q: %v", compositeFile, err) - } - defer compositeReader.Close() - - compositePath, err := filepath.Abs(filepath.Dir(compositeFile)) - if err != nil { - log.Fatalf("getting absolute path of composite config file %q: %v", compositeFile, err) - } - // catalog maintainer's 'catalogs.yaml' file tempCatalog, err := composite.FetchCatalogConfig(catalogFile, http.DefaultClient) if err != nil { @@ -62,22 +52,34 @@ and a 'composite template' file`, } defer tempCatalog.Close() - template := composite.NewTemplate( - composite.WithCatalogFile(tempCatalog), - composite.WithContributionFile(compositeReader, compositePath), - composite.WithOutputType(output), - composite.WithRegistry(reg), - ) + for _, contribution := range args { + // operator author's 'composite.yaml' file + compositeReader, err := os.Open(contribution) + if err != nil { + log.Fatalf("opening composite config file %q: %v", contribution, err) + } + defer compositeReader.Close() - err = template.Render(cmd.Context(), validate) - if err != nil { - log.Fatalf("rendering the composite template: %v", err) + compositePath, err := filepath.Abs(filepath.Dir(contribution)) + if err != nil { + log.Fatalf("getting absolute path of composite config file %q: %v", contribution, err) + } + + template := composite.NewTemplate( + composite.WithCatalogFile(tempCatalog), + composite.WithContributionFile(compositeReader, compositePath), + composite.WithOutputType(output), + composite.WithRegistry(reg), + ) + + err = template.Render(cmd.Context(), validate) + if err != nil { + log.Fatalf("rendering the composite template: %v", err) + } } }, } - cmd.Flags().StringVarP(&output, "output", "o", "json", "Output format (json|yaml)") cmd.Flags().BoolVar(&validate, "validate", true, "whether or not the created FBC should be validated (i.e 'opm validate')") - cmd.Flags().StringVarP(&compositeFile, "composite-config", "c", "composite.yaml", "File to use as the composite configuration file") cmd.Flags().StringVarP(&catalogFile, "catalog-config", "f", "catalogs.yaml", "File to use as the catalog configuration file") return cmd } diff --git a/cmd/opm/alpha/template/semver.go b/cmd/opm/alpha/template/semver.go index f67f9596c0..739a3db14f 100644 --- a/cmd/opm/alpha/template/semver.go +++ b/cmd/opm/alpha/template/semver.go @@ -15,7 +15,6 @@ import ( ) func newSemverTemplateCmd() *cobra.Command { - output := "" cmd := &cobra.Command{ Use: "semver [FILE]", Short: `Generate a file-based catalog from a single 'semver template' file @@ -35,6 +34,10 @@ When FILE is '-' or not provided, the template is read from standard input`, defer data.Close() var write func(declcfg.DeclarativeConfig, io.Writer) error + output, err := cmd.Flags().GetString("output") + if err != nil { + log.Fatalf("unable to determine output format") + } switch output { case "json": write = declcfg.WriteJSON @@ -79,6 +82,5 @@ When FILE is '-' or not provided, the template is read from standard input`, }, } - cmd.Flags().StringVarP(&output, "output", "o", "json", "Output format (json|yaml|mermaid)") return cmd }