Skip to content

Commit d5ef52d

Browse files
committed
refactor to move more processing to the template
Signed-off-by: Jordan Keister <[email protected]>
1 parent 49eae7b commit d5ef52d

File tree

4 files changed

+181
-163
lines changed

4 files changed

+181
-163
lines changed

alpha/template/composite/builder.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ type ContainerConfig struct {
3636
type BuilderConfig struct {
3737
ContainerCfg ContainerConfig
3838
OutputType string
39-
InputDirectory string
4039
}
4140

4241
type Builder interface {

alpha/template/composite/builder_test.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,6 @@ func TestBasicBuilder(t *testing.T) {
244244
}
245245

246246
for i, tc := range testCases {
247-
tc.basicBuilder.builderCfg.InputDirectory = testDir
248247
t.Run(tc.name, func(t *testing.T) {
249248
outDir := fmt.Sprintf("basic-%d", i)
250249
outPath := path.Join(testDir, outDir)
@@ -656,7 +655,6 @@ func TestSemverBuilder(t *testing.T) {
656655
}
657656

658657
for i, tc := range testCases {
659-
tc.semverBuilder.builderCfg.InputDirectory = testDir
660658
t.Run(tc.name, func(t *testing.T) {
661659
outDir := fmt.Sprintf("semver-%d", i)
662660
outPath := path.Join(testDir, outDir)
@@ -1077,7 +1075,6 @@ func TestRawBuilder(t *testing.T) {
10771075
}
10781076

10791077
for i, tc := range testCases {
1080-
tc.rawBuilder.builderCfg.InputDirectory = testDir
10811078
t.Run(tc.name, func(t *testing.T) {
10821079
outDir := fmt.Sprintf("raw-%d", i)
10831080
outPath := path.Join(testDir, outDir)
@@ -1505,7 +1502,6 @@ func TestCustomBuilder(t *testing.T) {
15051502
}
15061503

15071504
for i, tc := range testCases {
1508-
tc.customBuilder.builderCfg.InputDirectory = testDir
15091505
t.Run(tc.name, func(t *testing.T) {
15101506
outDir := fmt.Sprintf("custom-%d", i)
15111507
outPath := path.Join(testDir, outDir)

alpha/template/composite/composite.go

Lines changed: 175 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,50 @@ package composite
22

33
import (
44
"context"
5+
"encoding/json"
56
"fmt"
7+
"io"
8+
"net/http"
9+
"net/url"
10+
"os"
611

712
"github.com/operator-framework/operator-registry/pkg/image"
13+
"k8s.io/apimachinery/pkg/util/yaml"
814
)
915

1016
type BuilderMap map[string]Builder
1117

1218
type CatalogBuilderMap map[string]BuilderMap
1319

1420
type Template struct {
15-
CatalogBuilders CatalogBuilderMap
16-
Registry image.Registry
21+
CatalogFile string
22+
ContributionFile string
23+
Validate bool
24+
OutputType string
25+
Registry image.Registry
1726
}
1827

1928
// TODO(everettraven): do we need the context here? If so, how should it be used?
20-
func (t *Template) Render(ctx context.Context, config *CompositeConfig, validate bool) error {
29+
func (t *Template) Render(ctx context.Context, validate bool) error {
30+
31+
catalogFile, err := t.parseCatalogsSpec()
32+
if err != nil {
33+
return err
34+
}
35+
36+
contributionFile, err := t.parseContributionSpec()
37+
if err != nil {
38+
return err
39+
}
40+
41+
catalogBuilderMap, err := t.newCatalogBuilderMap(catalogFile.Catalogs, t.OutputType)
42+
if err != nil {
43+
return err
44+
}
45+
2146
// TODO(everettraven): should we return aggregated errors?
22-
for _, component := range config.Components {
23-
if builderMap, ok := t.CatalogBuilders[component.Name]; ok {
47+
for _, component := range contributionFile.Components {
48+
if builderMap, ok := (*catalogBuilderMap)[component.Name]; ok {
2449
if builder, ok := builderMap[component.Strategy.Template.Schema]; ok {
2550
// run the builder corresponding to the schema
2651
err := builder.Build(ctx, t.Registry, component.Destination.Path, component.Strategy.Template)
@@ -40,11 +65,155 @@ func (t *Template) Render(ctx context.Context, config *CompositeConfig, validate
4065
}
4166
} else {
4267
allowedComponents := []string{}
43-
for k := range t.CatalogBuilders {
68+
for k := range builderMap {
4469
allowedComponents = append(allowedComponents, k)
4570
}
4671
return fmt.Errorf("building component %q: component does not exist in the catalog configuration. Available components are: %s", component.Name, allowedComponents)
4772
}
4873
}
4974
return nil
5075
}
76+
77+
func builderForSchema(schema string, builderCfg BuilderConfig) (Builder, error) {
78+
var builder Builder
79+
switch schema {
80+
case BasicBuilderSchema:
81+
builder = NewBasicBuilder(builderCfg)
82+
case SemverBuilderSchema:
83+
builder = NewSemverBuilder(builderCfg)
84+
case RawBuilderSchema:
85+
builder = NewRawBuilder(builderCfg)
86+
case CustomBuilderSchema:
87+
builder = NewCustomBuilder(builderCfg)
88+
default:
89+
return nil, fmt.Errorf("unknown schema %q", schema)
90+
}
91+
92+
return builder, nil
93+
}
94+
95+
func (t *Template) parseCatalogsSpec() (*CatalogConfig, error) {
96+
var tempCatalog io.ReadCloser
97+
catalogURI, err := url.ParseRequestURI(t.CatalogFile)
98+
if err != nil {
99+
tempCatalog, err = os.Open(t.CatalogFile)
100+
if err != nil {
101+
return nil, fmt.Errorf("opening catalog config file %q: %v", t.CatalogFile, err)
102+
}
103+
defer tempCatalog.Close()
104+
} else {
105+
tempResp, err := http.Get(catalogURI.String())
106+
if err != nil {
107+
return nil, fmt.Errorf("fetching remote catalog config file %q: %v", t.CatalogFile, err)
108+
}
109+
tempCatalog = tempResp.Body
110+
defer tempCatalog.Close()
111+
}
112+
catalogData := tempCatalog
113+
114+
// get catalog configurations
115+
catalogConfig := &CatalogConfig{}
116+
catalogDoc := json.RawMessage{}
117+
catalogDecoder := yaml.NewYAMLOrJSONDecoder(catalogData, 4096)
118+
err = catalogDecoder.Decode(&catalogDoc)
119+
if err != nil {
120+
return nil, fmt.Errorf("decoding catalog config: %v", err)
121+
}
122+
err = json.Unmarshal(catalogDoc, catalogConfig)
123+
if err != nil {
124+
return nil, fmt.Errorf("unmarshalling catalog config: %v", err)
125+
}
126+
127+
if catalogConfig.Schema != CatalogSchema {
128+
return nil, fmt.Errorf("catalog configuration file has unknown schema, should be %q", CatalogSchema)
129+
}
130+
131+
return catalogConfig, nil
132+
}
133+
134+
func (t *Template) parseContributionSpec() (*CompositeConfig, error) {
135+
136+
compositeData, err := os.Open(t.ContributionFile)
137+
if err != nil {
138+
return nil, fmt.Errorf("opening composite config file %q: %v", t.ContributionFile, err)
139+
}
140+
defer compositeData.Close()
141+
142+
// parse data to composite config
143+
compositeConfig := &CompositeConfig{}
144+
compositeDoc := json.RawMessage{}
145+
compositeDecoder := yaml.NewYAMLOrJSONDecoder(compositeData, 4096)
146+
err = compositeDecoder.Decode(&compositeDoc)
147+
if err != nil {
148+
return nil, fmt.Errorf("decoding composite config: %v", err)
149+
}
150+
err = json.Unmarshal(compositeDoc, compositeConfig)
151+
if err != nil {
152+
return nil, fmt.Errorf("unmarshalling composite config: %v", err)
153+
}
154+
155+
if compositeConfig.Schema != CompositeSchema {
156+
return nil, fmt.Errorf("%q has unknown schema, should be %q", t.ContributionFile, CompositeSchema)
157+
}
158+
159+
return compositeConfig, nil
160+
}
161+
162+
func (t *Template) newCatalogBuilderMap(catalogs []Catalog, outputType string) (*CatalogBuilderMap, error) {
163+
164+
catalogBuilderMap := make(CatalogBuilderMap)
165+
166+
// setup the builders for each catalog
167+
setupFailed := false
168+
setupErrors := map[string][]string{}
169+
for _, catalog := range catalogs {
170+
errs := []string{}
171+
if catalog.Destination.BaseImage == "" {
172+
errs = append(errs, "destination.baseImage must not be an empty string")
173+
}
174+
175+
if catalog.Destination.WorkingDir == "" {
176+
errs = append(errs, "destination.workingDir must not be an empty string")
177+
}
178+
179+
// check for validation errors and skip builder creation if there are any errors
180+
if len(errs) > 0 {
181+
setupFailed = true
182+
setupErrors[catalog.Name] = errs
183+
continue
184+
}
185+
186+
if _, ok := catalogBuilderMap[catalog.Name]; !ok {
187+
builderMap := make(BuilderMap)
188+
for _, schema := range catalog.Builders {
189+
builder, err := builderForSchema(schema, BuilderConfig{
190+
ContainerCfg: ContainerConfig{
191+
BaseImage: catalog.Destination.BaseImage,
192+
WorkingDir: catalog.Destination.WorkingDir,
193+
},
194+
OutputType: outputType,
195+
})
196+
if err != nil {
197+
return nil, fmt.Errorf("getting builder %q for catalog %q: %v", schema, catalog.Name, err)
198+
}
199+
builderMap[schema] = builder
200+
}
201+
catalogBuilderMap[catalog.Name] = builderMap
202+
}
203+
}
204+
205+
// if there were errors validating the catalog configuration then exit
206+
if setupFailed {
207+
//build the error message
208+
var errMsg string
209+
for cat, errs := range setupErrors {
210+
errMsg += fmt.Sprintf("\nCatalog %v:\n", cat)
211+
for _, err := range errs {
212+
errMsg += fmt.Sprintf(" - %v\n", err)
213+
}
214+
}
215+
return nil, fmt.Errorf("catalog configuration file field validation failed: %s", errMsg)
216+
}
217+
218+
return &catalogBuilderMap, nil
219+
}

0 commit comments

Comments
 (0)