Skip to content

Commit dff7915

Browse files
authored
provide required schema for basic template and fbc conversion (#1335)
1 parent 965f085 commit dff7915

File tree

10 files changed

+218
-23
lines changed

10 files changed

+218
-23
lines changed

alpha/declcfg/declcfg.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ type ChannelEntry struct {
7070
// evaluation in bundlesEqual().
7171
type Bundle struct {
7272
Schema string `json:"schema"`
73-
Name string `json:"name"`
74-
Package string `json:"package"`
73+
Name string `json:"name,omitempty"`
74+
Package string `json:"package,omitempty"`
7575
Image string `json:"image"`
7676
Properties []property.Property `json:"properties,omitempty" hash:"set"`
7777
RelatedImages []RelatedImage `json:"relatedImages,omitempty" hash:"set"`

alpha/declcfg/load.go

+11
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,17 @@ func LoadFile(root fs.FS, path string) (*DeclarativeConfig, error) {
259259
return cfg, nil
260260
}
261261

262+
// LoadSlice will compose declarative config components from a slice of Meta objects
263+
func LoadSlice(metas []*Meta) (*DeclarativeConfig, error) {
264+
builder := fbcBuilder{}
265+
for _, meta := range metas {
266+
if err := builder.addMeta(meta); err != nil {
267+
return nil, err
268+
}
269+
}
270+
return &builder.cfg, nil
271+
}
272+
262273
type fbcBuilder struct {
263274
cfg DeclarativeConfig
264275

alpha/template/basic/basic.go

+70-2
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,58 @@ package basic
22

33
import (
44
"context"
5+
"encoding/json"
56
"fmt"
67
"io"
78

89
"github.com/operator-framework/operator-registry/alpha/action"
910
"github.com/operator-framework/operator-registry/alpha/declcfg"
1011
"github.com/operator-framework/operator-registry/pkg/image"
12+
"k8s.io/apimachinery/pkg/util/yaml"
1113
)
1214

15+
const schema string = "olm.template.basic"
16+
1317
type Template struct {
1418
Registry image.Registry
1519
}
1620

21+
type BasicTemplate struct {
22+
Schema string `json:"schema"`
23+
Entries []*declcfg.Meta `json:"entries"`
24+
}
25+
26+
func parseSpec(reader io.Reader) (*BasicTemplate, error) {
27+
bt := &BasicTemplate{}
28+
btDoc := json.RawMessage{}
29+
btDecoder := yaml.NewYAMLOrJSONDecoder(reader, 4096)
30+
err := btDecoder.Decode(&btDoc)
31+
if err != nil {
32+
return nil, fmt.Errorf("decoding template schema: %v", err)
33+
}
34+
err = json.Unmarshal(btDoc, bt)
35+
if err != nil {
36+
return nil, fmt.Errorf("unmarshalling template: %v", err)
37+
}
38+
39+
if bt.Schema != schema {
40+
return nil, fmt.Errorf("template has unknown schema (%q), should be %q", bt.Schema, schema)
41+
}
42+
43+
return bt, nil
44+
}
45+
1746
func (t Template) Render(ctx context.Context, reader io.Reader) (*declcfg.DeclarativeConfig, error) {
18-
cfg, err := declcfg.LoadReader(reader)
47+
bt, err := parseSpec(reader)
48+
if err != nil {
49+
return nil, err
50+
}
51+
cfg, err := declcfg.LoadSlice(bt.Entries)
1952
if err != nil {
2053
return cfg, err
2154
}
2255

23-
outb := cfg.Bundles[:0] // allocate based on max size of input, but empty slice
56+
outb := cfg.Bundles[:0]
2457
// populate registry, incl any flags from CLI, and enforce only rendering bundle images
2558
r := action.Render{
2659
Registry: t.Registry,
@@ -48,3 +81,38 @@ func (t Template) Render(ctx context.Context, reader io.Reader) (*declcfg.Declar
4881
func isBundleTemplate(b *declcfg.Bundle) bool {
4982
return b.Schema != "" && b.Image != "" && b.Package == "" && len(b.Properties) == 0 && len(b.RelatedImages) == 0
5083
}
84+
85+
// FromReader reads FBC from a reader and generates a BasicTemplate from it
86+
func FromReader(r io.Reader) (*BasicTemplate, error) {
87+
var entries []*declcfg.Meta
88+
if err := declcfg.WalkMetasReader(r, func(meta *declcfg.Meta, err error) error {
89+
if err != nil {
90+
return err
91+
}
92+
if meta.Schema == declcfg.SchemaBundle {
93+
var b declcfg.Bundle
94+
if err := json.Unmarshal(meta.Blob, &b); err != nil {
95+
return fmt.Errorf("parse bundle: %v", err)
96+
}
97+
b2 := declcfg.Bundle{
98+
Schema: b.Schema,
99+
Image: b.Image,
100+
}
101+
meta.Blob, err = json.Marshal(b2)
102+
if err != nil {
103+
return fmt.Errorf("re-serialize bundle: %v", err)
104+
}
105+
}
106+
entries = append(entries, meta)
107+
return nil
108+
}); err != nil {
109+
return nil, err
110+
}
111+
112+
bt := &BasicTemplate{
113+
Schema: schema,
114+
Entries: entries,
115+
}
116+
117+
return bt, nil
118+
}

alpha/template/converter/converter.go

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package converter
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io"
7+
"os"
8+
9+
"github.com/operator-framework/operator-registry/alpha/template/basic"
10+
"github.com/operator-framework/operator-registry/pkg/image"
11+
"sigs.k8s.io/yaml"
12+
)
13+
14+
type Converter struct {
15+
FbcReader io.Reader
16+
OutputFormat string
17+
Registry image.Registry
18+
}
19+
20+
func (c *Converter) Convert() error {
21+
bt, err := basic.FromReader(c.FbcReader)
22+
if err != nil {
23+
return err
24+
}
25+
26+
b, _ := json.MarshalIndent(bt, "", " ")
27+
if c.OutputFormat == "json" {
28+
fmt.Fprintln(os.Stdout, string(b))
29+
} else {
30+
y, err := yaml.JSONToYAML(b)
31+
if err != nil {
32+
return err
33+
}
34+
y = append([]byte("---\n"), y...)
35+
fmt.Fprintln(os.Stdout, string(y))
36+
}
37+
38+
return nil
39+
}

cmd/opm/alpha/cmd.go

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"github.com/spf13/cobra"
55

66
"github.com/operator-framework/operator-registry/cmd/opm/alpha/bundle"
7+
converttemplate "github.com/operator-framework/operator-registry/cmd/opm/alpha/convert-template"
78
"github.com/operator-framework/operator-registry/cmd/opm/alpha/list"
89
rendergraph "github.com/operator-framework/operator-registry/cmd/opm/alpha/render-graph"
910
"github.com/operator-framework/operator-registry/cmd/opm/alpha/template"
@@ -26,6 +27,7 @@ func NewCmd(showAlphaHelp bool) *cobra.Command {
2627
list.NewCmd(),
2728
rendergraph.NewCmd(),
2829
template.NewCmd(),
30+
converttemplate.NewCmd(),
2931
)
3032
return runCmd
3133
}
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package converttemplate
2+
3+
import (
4+
"fmt"
5+
"log"
6+
7+
"github.com/spf13/cobra"
8+
9+
"github.com/operator-framework/operator-registry/alpha/template/converter"
10+
"github.com/operator-framework/operator-registry/cmd/opm/internal/util"
11+
)
12+
13+
func NewCmd() *cobra.Command {
14+
cmd := &cobra.Command{
15+
Use: "convert-template",
16+
Short: "Convert existing FBC to a supported template type",
17+
}
18+
cmd.AddCommand(
19+
newBasicConvertCmd(),
20+
)
21+
return cmd
22+
}
23+
24+
func newBasicConvertCmd() *cobra.Command {
25+
var (
26+
converter converter.Converter
27+
output string
28+
)
29+
cmd := &cobra.Command{
30+
Use: "basic [<fbc-file> | -]",
31+
Args: cobra.MaximumNArgs(1),
32+
Short: "Generate a basic template from existing FBC",
33+
Long: `Generate a basic template from existing FBC.
34+
35+
This command outputs a basic catalog template to STDOUT from input FBC.
36+
If no argument is specified or is '-' input is assumed from STDIN.
37+
`,
38+
RunE: func(c *cobra.Command, args []string) error {
39+
40+
switch output {
41+
case "yaml", "json":
42+
converter.OutputFormat = output
43+
default:
44+
log.Fatalf("invalid --output value %q, expected (json|yaml)", output)
45+
}
46+
47+
reader, name, err := util.OpenFileOrStdin(c, args)
48+
if err != nil {
49+
return fmt.Errorf("unable to open input: %q", name)
50+
}
51+
52+
converter.FbcReader = reader
53+
err = converter.Convert()
54+
if err != nil {
55+
return fmt.Errorf("converting: %v", err)
56+
}
57+
58+
return nil
59+
},
60+
}
61+
cmd.Flags().StringVarP(&output, "output", "o", "json", "Output format (json|yaml)")
62+
63+
return cmd
64+
}

cmd/opm/alpha/template/basic.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616
func newBasicTemplateCmd() *cobra.Command {
1717
var (
1818
template basic.Template
19-
output string
2019
)
2120
cmd := &cobra.Command{
2221
Use: "basic basic-template-file",
@@ -30,13 +29,17 @@ When FILE is '-' or not provided, the template is read from standard input`,
3029
// When no arguments or "-" is passed to the command,
3130
// assume input is coming from stdin
3231
// Otherwise open the file passed to the command
33-
data, source, err := openFileOrStdin(cmd, args)
32+
data, source, err := util.OpenFileOrStdin(cmd, args)
3433
if err != nil {
3534
log.Fatalf("unable to open %q: %v", source, err)
3635
}
3736
defer data.Close()
3837

3938
var write func(declcfg.DeclarativeConfig, io.Writer) error
39+
output, err := cmd.Flags().GetString("output")
40+
if err != nil {
41+
log.Fatalf("unable to determine output format")
42+
}
4043
switch output {
4144
case "yaml":
4245
write = declcfg.WriteYAML
@@ -70,6 +73,5 @@ When FILE is '-' or not provided, the template is read from standard input`,
7073
}
7174
},
7275
}
73-
cmd.Flags().StringVarP(&output, "output", "o", "json", "Output format (json|yaml)")
7476
return cmd
7577
}

cmd/opm/alpha/template/cmd.go

+11-13
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,27 @@
11
package template
22

33
import (
4-
"io"
5-
"os"
6-
74
"github.com/spf13/cobra"
85
)
96

107
func NewCmd() *cobra.Command {
8+
var output string
9+
1110
runCmd := &cobra.Command{
1211
Use: "render-template",
1312
Short: "Render a catalog template type",
1413
Args: cobra.NoArgs,
1514
}
1615

17-
runCmd.AddCommand(newBasicTemplateCmd())
18-
runCmd.AddCommand(newSemverTemplateCmd())
16+
bc := newBasicTemplateCmd()
17+
// bc.Hidden = true
18+
runCmd.AddCommand(bc)
1919

20-
return runCmd
21-
}
20+
sc := newSemverTemplateCmd()
21+
// sc.Hidden = true
22+
runCmd.AddCommand(sc)
2223

23-
func openFileOrStdin(cmd *cobra.Command, args []string) (io.ReadCloser, string, error) {
24-
if len(args) == 0 || args[0] == "-" {
25-
return io.NopCloser(cmd.InOrStdin()), "stdin", nil
26-
}
27-
reader, err := os.Open(args[0])
28-
return reader, args[0], err
24+
runCmd.PersistentFlags().StringVarP(&output, "output", "o", "json", "Output format (json|yaml)")
25+
26+
return runCmd
2927
}

cmd/opm/alpha/template/semver.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
)
1616

1717
func newSemverTemplateCmd() *cobra.Command {
18-
output := ""
1918
cmd := &cobra.Command{
2019
Use: "semver [FILE]",
2120
Short: `Generate a file-based catalog from a single 'semver template' file
@@ -28,13 +27,17 @@ When FILE is '-' or not provided, the template is read from standard input`,
2827
// When no arguments or "-" is passed to the command,
2928
// assume input is coming from stdin
3029
// Otherwise open the file passed to the command
31-
data, source, err := openFileOrStdin(cmd, args)
30+
data, source, err := util.OpenFileOrStdin(cmd, args)
3231
if err != nil {
3332
return err
3433
}
3534
defer data.Close()
3635

3736
var write func(declcfg.DeclarativeConfig, io.Writer) error
37+
output, err := cmd.Flags().GetString("output")
38+
if err != nil {
39+
log.Fatalf("unable to determine output format")
40+
}
3841
switch output {
3942
case "json":
4043
write = declcfg.WriteJSON
@@ -79,6 +82,5 @@ When FILE is '-' or not provided, the template is read from standard input`,
7982
},
8083
}
8184

82-
cmd.Flags().StringVarP(&output, "output", "o", "json", "Output format (json|yaml|mermaid)")
8385
return cmd
8486
}

cmd/opm/internal/util/util.go

+9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package util
22

33
import (
44
"errors"
5+
"io"
56
"os"
67

78
"github.com/spf13/cobra"
@@ -66,3 +67,11 @@ func CreateCLIRegistry(cmd *cobra.Command) (*containerdregistry.Registry, error)
6667
}
6768
return reg, nil
6869
}
70+
71+
func OpenFileOrStdin(cmd *cobra.Command, args []string) (io.ReadCloser, string, error) {
72+
if len(args) == 0 || args[0] == "-" {
73+
return io.NopCloser(cmd.InOrStdin()), "stdin", nil
74+
}
75+
reader, err := os.Open(args[0])
76+
return reader, args[0], err
77+
}

0 commit comments

Comments
 (0)