Skip to content

Commit

Permalink
provide required schema for basic template and fbc conversion (#1335)
Browse files Browse the repository at this point in the history
  • Loading branch information
grokspawn authored Jun 27, 2024
1 parent 965f085 commit dff7915
Show file tree
Hide file tree
Showing 10 changed files with 218 additions and 23 deletions.
4 changes: 2 additions & 2 deletions alpha/declcfg/declcfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ type ChannelEntry struct {
// evaluation in bundlesEqual().
type Bundle struct {
Schema string `json:"schema"`
Name string `json:"name"`
Package string `json:"package"`
Name string `json:"name,omitempty"`
Package string `json:"package,omitempty"`
Image string `json:"image"`
Properties []property.Property `json:"properties,omitempty" hash:"set"`
RelatedImages []RelatedImage `json:"relatedImages,omitempty" hash:"set"`
Expand Down
11 changes: 11 additions & 0 deletions alpha/declcfg/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,17 @@ func LoadFile(root fs.FS, path string) (*DeclarativeConfig, error) {
return cfg, nil
}

// LoadSlice will compose declarative config components from a slice of Meta objects
func LoadSlice(metas []*Meta) (*DeclarativeConfig, error) {
builder := fbcBuilder{}
for _, meta := range metas {
if err := builder.addMeta(meta); err != nil {
return nil, err
}
}
return &builder.cfg, nil
}

type fbcBuilder struct {
cfg DeclarativeConfig

Expand Down
72 changes: 70 additions & 2 deletions alpha/template/basic/basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,58 @@ package basic

import (
"context"
"encoding/json"
"fmt"
"io"

"github.com/operator-framework/operator-registry/alpha/action"
"github.com/operator-framework/operator-registry/alpha/declcfg"
"github.com/operator-framework/operator-registry/pkg/image"
"k8s.io/apimachinery/pkg/util/yaml"
)

const schema string = "olm.template.basic"

type Template struct {
Registry image.Registry
}

type BasicTemplate struct {
Schema string `json:"schema"`
Entries []*declcfg.Meta `json:"entries"`
}

func parseSpec(reader io.Reader) (*BasicTemplate, error) {
bt := &BasicTemplate{}
btDoc := json.RawMessage{}
btDecoder := yaml.NewYAMLOrJSONDecoder(reader, 4096)
err := btDecoder.Decode(&btDoc)
if err != nil {
return nil, fmt.Errorf("decoding template schema: %v", err)
}
err = json.Unmarshal(btDoc, bt)
if err != nil {
return nil, fmt.Errorf("unmarshalling template: %v", err)
}

if bt.Schema != schema {
return nil, fmt.Errorf("template has unknown schema (%q), should be %q", bt.Schema, schema)
}

return bt, nil
}

func (t Template) Render(ctx context.Context, reader io.Reader) (*declcfg.DeclarativeConfig, error) {
cfg, err := declcfg.LoadReader(reader)
bt, err := parseSpec(reader)
if err != nil {
return nil, err
}
cfg, err := declcfg.LoadSlice(bt.Entries)
if err != nil {
return cfg, err
}

outb := cfg.Bundles[:0] // allocate based on max size of input, but empty slice
outb := cfg.Bundles[:0]
// populate registry, incl any flags from CLI, and enforce only rendering bundle images
r := action.Render{
Registry: t.Registry,
Expand Down Expand Up @@ -48,3 +81,38 @@ func (t Template) Render(ctx context.Context, reader io.Reader) (*declcfg.Declar
func isBundleTemplate(b *declcfg.Bundle) bool {
return b.Schema != "" && b.Image != "" && b.Package == "" && len(b.Properties) == 0 && len(b.RelatedImages) == 0
}

// FromReader reads FBC from a reader and generates a BasicTemplate from it
func FromReader(r io.Reader) (*BasicTemplate, error) {
var entries []*declcfg.Meta
if err := declcfg.WalkMetasReader(r, func(meta *declcfg.Meta, err error) error {
if err != nil {
return err
}
if meta.Schema == declcfg.SchemaBundle {
var b declcfg.Bundle
if err := json.Unmarshal(meta.Blob, &b); err != nil {
return fmt.Errorf("parse bundle: %v", err)
}
b2 := declcfg.Bundle{
Schema: b.Schema,
Image: b.Image,
}
meta.Blob, err = json.Marshal(b2)
if err != nil {
return fmt.Errorf("re-serialize bundle: %v", err)
}
}
entries = append(entries, meta)
return nil
}); err != nil {
return nil, err
}

bt := &BasicTemplate{
Schema: schema,
Entries: entries,
}

return bt, nil
}
39 changes: 39 additions & 0 deletions alpha/template/converter/converter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package converter

import (
"encoding/json"
"fmt"
"io"
"os"

"github.com/operator-framework/operator-registry/alpha/template/basic"
"github.com/operator-framework/operator-registry/pkg/image"
"sigs.k8s.io/yaml"
)

type Converter struct {
FbcReader io.Reader
OutputFormat string
Registry image.Registry
}

func (c *Converter) Convert() error {
bt, err := basic.FromReader(c.FbcReader)
if err != nil {
return err
}

b, _ := json.MarshalIndent(bt, "", " ")
if c.OutputFormat == "json" {
fmt.Fprintln(os.Stdout, string(b))
} else {
y, err := yaml.JSONToYAML(b)
if err != nil {
return err
}
y = append([]byte("---\n"), y...)
fmt.Fprintln(os.Stdout, string(y))
}

return nil
}
2 changes: 2 additions & 0 deletions cmd/opm/alpha/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/spf13/cobra"

"github.com/operator-framework/operator-registry/cmd/opm/alpha/bundle"
converttemplate "github.com/operator-framework/operator-registry/cmd/opm/alpha/convert-template"
"github.com/operator-framework/operator-registry/cmd/opm/alpha/list"
rendergraph "github.com/operator-framework/operator-registry/cmd/opm/alpha/render-graph"
"github.com/operator-framework/operator-registry/cmd/opm/alpha/template"
Expand All @@ -26,6 +27,7 @@ func NewCmd(showAlphaHelp bool) *cobra.Command {
list.NewCmd(),
rendergraph.NewCmd(),
template.NewCmd(),
converttemplate.NewCmd(),
)
return runCmd
}
64 changes: 64 additions & 0 deletions cmd/opm/alpha/convert-template/convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package converttemplate

import (
"fmt"
"log"

"github.com/spf13/cobra"

"github.com/operator-framework/operator-registry/alpha/template/converter"
"github.com/operator-framework/operator-registry/cmd/opm/internal/util"
)

func NewCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "convert-template",
Short: "Convert existing FBC to a supported template type",
}
cmd.AddCommand(
newBasicConvertCmd(),
)
return cmd
}

func newBasicConvertCmd() *cobra.Command {
var (
converter converter.Converter
output string
)
cmd := &cobra.Command{
Use: "basic [<fbc-file> | -]",
Args: cobra.MaximumNArgs(1),
Short: "Generate a basic template from existing FBC",
Long: `Generate a basic template from existing FBC.
This command outputs a basic catalog template to STDOUT from input FBC.
If no argument is specified or is '-' input is assumed from STDIN.
`,
RunE: func(c *cobra.Command, args []string) error {

switch output {
case "yaml", "json":
converter.OutputFormat = output
default:
log.Fatalf("invalid --output value %q, expected (json|yaml)", output)
}

reader, name, err := util.OpenFileOrStdin(c, args)
if err != nil {
return fmt.Errorf("unable to open input: %q", name)
}

converter.FbcReader = reader
err = converter.Convert()
if err != nil {
return fmt.Errorf("converting: %v", err)
}

return nil
},
}
cmd.Flags().StringVarP(&output, "output", "o", "json", "Output format (json|yaml)")

return cmd
}
8 changes: 5 additions & 3 deletions cmd/opm/alpha/template/basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
func newBasicTemplateCmd() *cobra.Command {
var (
template basic.Template
output string
)
cmd := &cobra.Command{
Use: "basic basic-template-file",
Expand All @@ -30,13 +29,17 @@ When FILE is '-' or not provided, the template is read from standard input`,
// 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)
data, source, err := util.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
output, err := cmd.Flags().GetString("output")
if err != nil {
log.Fatalf("unable to determine output format")
}
switch output {
case "yaml":
write = declcfg.WriteYAML
Expand Down Expand Up @@ -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
}
24 changes: 11 additions & 13 deletions cmd/opm/alpha/template/cmd.go
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
package template

import (
"io"
"os"

"github.com/spf13/cobra"
)

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())
bc := newBasicTemplateCmd()
// bc.Hidden = true
runCmd.AddCommand(bc)

return runCmd
}
sc := newSemverTemplateCmd()
// sc.Hidden = true
runCmd.AddCommand(sc)

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
runCmd.PersistentFlags().StringVarP(&output, "output", "o", "json", "Output format (json|yaml)")

return runCmd
}
8 changes: 5 additions & 3 deletions cmd/opm/alpha/template/semver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -28,13 +27,17 @@ When FILE is '-' or not provided, the template is read from standard input`,
// 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)
data, source, err := util.OpenFileOrStdin(cmd, args)
if err != nil {
return err
}
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
Expand Down Expand Up @@ -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
}
9 changes: 9 additions & 0 deletions cmd/opm/internal/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package util

import (
"errors"
"io"
"os"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -66,3 +67,11 @@ func CreateCLIRegistry(cmd *cobra.Command) (*containerdregistry.Registry, error)
}
return reg, nil
}

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
}

0 comments on commit dff7915

Please sign in to comment.