Skip to content

Commit cdde802

Browse files
committed
replaces via substitutes-for template
Signed-off-by: grokspawn <jordan@nimblewidget.com>
1 parent 78826e7 commit cdde802

File tree

3 files changed

+172
-0
lines changed

3 files changed

+172
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package substitutes
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
9+
"k8s.io/apimachinery/pkg/util/yaml"
10+
11+
"github.com/operator-framework/operator-registry/alpha/declcfg"
12+
)
13+
14+
type Template struct {
15+
RenderBundle func(context.Context, string) (*declcfg.DeclarativeConfig, error)
16+
}
17+
18+
type SubstitutesTemplate struct {
19+
Schema string `json:"schema"`
20+
Entries []*declcfg.Meta `json:"entries"`
21+
Substitutes []string `json:"substitutes"`
22+
}
23+
24+
const schema string = "olm.template.substitutes"
25+
26+
func parseSpec(reader io.Reader) (*SubstitutesTemplate, error) {
27+
st := &SubstitutesTemplate{}
28+
stDoc := json.RawMessage{}
29+
stDecoder := yaml.NewYAMLOrJSONDecoder(reader, 4096)
30+
err := stDecoder.Decode(&stDoc)
31+
if err != nil {
32+
return nil, fmt.Errorf("decoding template schema: %v", err)
33+
}
34+
err = json.Unmarshal(stDoc, st)
35+
if err != nil {
36+
return nil, fmt.Errorf("unmarshalling template: %v", err)
37+
}
38+
39+
if st.Schema != schema {
40+
return nil, fmt.Errorf("template has unknown schema (%q), should be %q", st.Schema, schema)
41+
}
42+
43+
return st, nil
44+
}
45+
46+
func (t Template) Render(ctx context.Context, reader io.Reader) (*declcfg.DeclarativeConfig, error) {
47+
st, err := parseSpec(reader)
48+
if err != nil {
49+
return nil, fmt.Errorf("render: unable to parse template: %v", err)
50+
}
51+
52+
// TODO: Implement the actual rendering logic using st.Entries and st.Substitutes
53+
_ = st
54+
return nil, nil
55+
}

cmd/opm/alpha/template/cmd.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ func NewCmd() *cobra.Command {
2121
// sc.Hidden = true
2222
runCmd.AddCommand(sc)
2323

24+
subs := newSubstitutesForTemplateCmd()
25+
// subs.Hidden = true
26+
runCmd.AddCommand(subs)
27+
2428
runCmd.PersistentFlags().StringVarP(&output, "output", "o", "json", "Output format (json|yaml)")
2529

2630
return runCmd
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package template
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io"
7+
"log"
8+
"os"
9+
10+
"github.com/sirupsen/logrus"
11+
"github.com/spf13/cobra"
12+
13+
"github.com/operator-framework/operator-registry/alpha/action"
14+
"github.com/operator-framework/operator-registry/alpha/action/migrations"
15+
"github.com/operator-framework/operator-registry/alpha/declcfg"
16+
"github.com/operator-framework/operator-registry/alpha/template/substitutes"
17+
"github.com/operator-framework/operator-registry/cmd/opm/internal/util"
18+
)
19+
20+
func newSubstitutesForTemplateCmd() *cobra.Command {
21+
var (
22+
migrateLevel string
23+
)
24+
25+
cmd := &cobra.Command{
26+
Use: "substitutes [FILE]",
27+
Short: `Generate a file-based catalog from a single 'substitutes template' file
28+
When FILE is '-' or not provided, the template is read from standard input`,
29+
Long: `Generate a file-based catalog from a single 'substitutes template' file
30+
When FILE is '-' or not provided, the template is read from standard input`,
31+
Args: cobra.MaximumNArgs(1),
32+
RunE: func(cmd *cobra.Command, args []string) error {
33+
// Handle different input argument types
34+
// When no arguments or "-" is passed to the command,
35+
// assume input is coming from stdin
36+
// Otherwise open the file passed to the command
37+
data, source, err := util.OpenFileOrStdin(cmd, args)
38+
if err != nil {
39+
return err
40+
}
41+
defer data.Close()
42+
43+
var write func(declcfg.DeclarativeConfig, io.Writer) error
44+
output, err := cmd.Flags().GetString("output")
45+
if err != nil {
46+
log.Fatalf("unable to determine output format")
47+
}
48+
switch output {
49+
case "json":
50+
write = declcfg.WriteJSON
51+
case "yaml":
52+
write = declcfg.WriteYAML
53+
case "mermaid":
54+
write = func(cfg declcfg.DeclarativeConfig, writer io.Writer) error {
55+
mermaidWriter := declcfg.NewMermaidWriter()
56+
return mermaidWriter.WriteChannels(cfg, writer)
57+
}
58+
default:
59+
return fmt.Errorf("invalid output format %q", output)
60+
}
61+
62+
// The bundle loading impl is somewhat verbose, even on the happy path,
63+
// so discard all logrus default logger logs. Any important failures will be
64+
// returned from template.Render and logged as fatal errors.
65+
logrus.SetOutput(io.Discard)
66+
67+
reg, err := util.CreateCLIRegistry(cmd)
68+
if err != nil {
69+
log.Fatalf("creating containerd registry: %v", err)
70+
}
71+
defer func() {
72+
_ = reg.Destroy()
73+
}()
74+
75+
var m *migrations.Migrations
76+
if migrateLevel != "" {
77+
m, err = migrations.NewMigrations(migrateLevel)
78+
if err != nil {
79+
log.Fatal(err)
80+
}
81+
}
82+
83+
template := substitutes.Template{
84+
RenderBundle: func(ctx context.Context, ref string) (*declcfg.DeclarativeConfig, error) {
85+
renderer := action.Render{
86+
Refs: []string{ref},
87+
Registry: reg,
88+
AllowedRefMask: action.RefBundleImage,
89+
Migrations: m,
90+
}
91+
return renderer.Run(ctx)
92+
},
93+
}
94+
95+
out, err := template.Render(cmd.Context(), data)
96+
if err != nil {
97+
log.Fatalf("substitutes %q: %v", source, err)
98+
}
99+
100+
if out != nil {
101+
if err := write(*out, os.Stdout); err != nil {
102+
log.Fatal(err)
103+
}
104+
}
105+
106+
return nil
107+
},
108+
}
109+
110+
cmd.Flags().StringVar(&migrateLevel, "migrate-level", "", "Name of the last migration to run (default: none)\n"+migrations.HelpText())
111+
112+
return cmd
113+
}

0 commit comments

Comments
 (0)