@@ -14,12 +14,55 @@ import (
1414
1515 "github.com/operator-framework/operator-registry/alpha/declcfg"
1616 "github.com/operator-framework/operator-registry/alpha/property"
17+ "github.com/operator-framework/operator-registry/alpha/template"
1718)
1819
19- func (t Template ) Render (ctx context.Context ) (* declcfg.DeclarativeConfig , error ) {
20+ // IO structs -- BEGIN
21+ type semverTemplateBundleEntry struct {
22+ Image string `json:"image,omitempty"`
23+ }
24+
25+ type semverTemplateChannelBundles struct {
26+ Bundles []semverTemplateBundleEntry `json:"bundles,omitempty"`
27+ }
28+
29+ type SemverTemplateData struct {
30+ Schema string `json:"schema"`
31+ GenerateMajorChannels bool `json:"generateMajorChannels,omitempty"`
32+ GenerateMinorChannels bool `json:"generateMinorChannels,omitempty"`
33+ DefaultChannelTypePreference streamType `json:"defaultChannelTypePreference,omitempty"`
34+ Candidate semverTemplateChannelBundles `json:"candidate,omitempty"`
35+ Fast semverTemplateChannelBundles `json:"fast,omitempty"`
36+ Stable semverTemplateChannelBundles `json:"stable,omitempty"`
37+
38+ pkg string `json:"-"` // the derived package name
39+ defaultChannel string `json:"-"` // detected "most stable" channel head
40+ }
41+
42+ // IO structs -- END
43+
44+ // SemverTemplate implements the common template interface
45+ type SemverTemplate struct {
46+ renderBundle template.BundleRenderer
47+ }
48+
49+ // NewTemplate creates a new semver template instance
50+ func NewTemplate (renderBundle template.BundleRenderer ) template.Template {
51+ return & SemverTemplate {
52+ renderBundle : renderBundle ,
53+ }
54+ }
55+
56+ // RenderBundle implements the template.Template interface
57+ func (t * SemverTemplate ) RenderBundle (ctx context.Context , image string ) (* declcfg.DeclarativeConfig , error ) {
58+ return t .renderBundle (ctx , image )
59+ }
60+
61+ // Render implements the template.Template interface
62+ func (t * SemverTemplate ) Render (ctx context.Context , reader io.Reader ) (* declcfg.DeclarativeConfig , error ) {
2063 var out declcfg.DeclarativeConfig
2164
22- sv , err := readFile (t . Data )
65+ sv , err := readFile (reader )
2366 if err != nil {
2467 return nil , fmt .Errorf ("render: unable to read file: %v" , err )
2568 }
@@ -57,7 +100,69 @@ func (t Template) Render(ctx context.Context) (*declcfg.DeclarativeConfig, error
57100 return & out , nil
58101}
59102
60- func buildBundleList (t semverTemplate ) map [string ]string {
103+ // Schema implements the template.Template interface
104+ func (t * SemverTemplate ) Schema () string {
105+ return schema
106+ }
107+
108+ // Factory implements the template.TemplateFactory interface
109+ type Factory struct {}
110+
111+ // CreateTemplate implements the template.TemplateFactory interface
112+ func (f * Factory ) CreateTemplate (renderBundle template.BundleRenderer ) template.Template {
113+ return NewTemplate (renderBundle )
114+ }
115+
116+ // Schema implements the template.TemplateFactory interface
117+ func (f * Factory ) Schema () string {
118+ return schema
119+ }
120+
121+ const schema string = "olm.semver"
122+
123+ // channel "archetypes", restricted in this iteration to just these
124+ type channelArchetype string
125+
126+ const (
127+ candidateChannelArchetype channelArchetype = "candidate"
128+ fastChannelArchetype channelArchetype = "fast"
129+ stableChannelArchetype channelArchetype = "stable"
130+ )
131+
132+ // mapping channel name --> stability, where higher values indicate greater stability
133+ var channelPriorities = map [channelArchetype ]int {candidateChannelArchetype : 0 , fastChannelArchetype : 1 , stableChannelArchetype : 2 }
134+
135+ // sorting capability for a slice according to the assigned channelPriorities
136+ type byChannelPriority []channelArchetype
137+
138+ func (b byChannelPriority ) Len () int { return len (b ) }
139+ func (b byChannelPriority ) Less (i , j int ) bool {
140+ return channelPriorities [b [i ]] < channelPriorities [b [j ]]
141+ }
142+ func (b byChannelPriority ) Swap (i , j int ) { b [i ], b [j ] = b [j ], b [i ] }
143+
144+ type streamType string
145+
146+ const defaultStreamType streamType = ""
147+ const minorStreamType streamType = "minor"
148+ const majorStreamType streamType = "major"
149+
150+ // general preference for minor channels
151+ var streamTypePriorities = map [streamType ]int {minorStreamType : 2 , majorStreamType : 1 , defaultStreamType : 0 }
152+
153+ // map of archetypes --> bundles --> bundle-version from the input file
154+ type bundleVersions map [channelArchetype ]map [string ]semver.Version // e.g. srcv["stable"]["example-operator.v1.0.0"] = 1.0.0
155+
156+ // the "high-water channel" struct functions as a freely-rising indicator of the "most stable" channel head, so we can use that
157+ // later as the package's defaultChannel attribute
158+ type highwaterChannel struct {
159+ archetype channelArchetype
160+ kind streamType
161+ version semver.Version
162+ name string
163+ }
164+
165+ func buildBundleList (t SemverTemplateData ) map [string ]string {
61166 dict := make (map [string ]string )
62167 for _ , bl := range []semverTemplateChannelBundles {t .Candidate , t .Fast , t .Stable } {
63168 for _ , b := range bl .Bundles {
@@ -69,13 +174,13 @@ func buildBundleList(t semverTemplate) map[string]string {
69174 return dict
70175}
71176
72- func readFile (reader io.Reader ) (* semverTemplate , error ) {
177+ func readFile (reader io.Reader ) (* SemverTemplateData , error ) {
73178 data , err := io .ReadAll (reader )
74179 if err != nil {
75180 return nil , err
76181 }
77182
78- sv := semverTemplate {}
183+ sv := SemverTemplateData {}
79184 if err := yaml .UnmarshalStrict (data , & sv ); err != nil {
80185 return nil , err
81186 }
@@ -114,7 +219,7 @@ func readFile(reader io.Reader) (*semverTemplate, error) {
114219 return & sv , nil
115220}
116221
117- func (sv * semverTemplate ) getVersionsFromStandardChannels (cfg * declcfg.DeclarativeConfig , bundleDict map [string ]string ) (* bundleVersions , error ) {
222+ func (sv * SemverTemplateData ) getVersionsFromStandardChannels (cfg * declcfg.DeclarativeConfig , bundleDict map [string ]string ) (* bundleVersions , error ) {
118223 versions := bundleVersions {}
119224
120225 bdm , err := sv .getVersionsFromChannel (sv .Candidate .Bundles , bundleDict , cfg )
@@ -147,7 +252,7 @@ func (sv *semverTemplate) getVersionsFromStandardChannels(cfg *declcfg.Declarati
147252 return & versions , nil
148253}
149254
150- func (sv * semverTemplate ) getVersionsFromChannel (semverBundles []semverTemplateBundleEntry , bundleDict map [string ]string , cfg * declcfg.DeclarativeConfig ) (map [string ]semver.Version , error ) {
255+ func (sv * SemverTemplateData ) getVersionsFromChannel (semverBundles []semverTemplateBundleEntry , bundleDict map [string ]string , cfg * declcfg.DeclarativeConfig ) (map [string ]semver.Version , error ) {
151256 entries := make (map [string ]semver.Version )
152257
153258 // we iterate over the channel bundles from the template, to:
@@ -209,7 +314,7 @@ func (sv *semverTemplate) getVersionsFromChannel(semverBundles []semverTemplateB
209314// - within the same minor version (Y-stream), the head of the channel should have a 'skips' encompassing all lesser Y.Z versions of the bundle enumerated in the template.
210315// along the way, uses a highwaterChannel marker to identify the "most stable" channel head to be used as the default channel for the generated package
211316
212- func (sv * semverTemplate ) generateChannels (semverChannels * bundleVersions ) []declcfg.Channel {
317+ func (sv * SemverTemplateData ) generateChannels (semverChannels * bundleVersions ) []declcfg.Channel {
213318 outChannels := []declcfg.Channel {}
214319
215320 // sort the channel archetypes in ascending order so we can traverse the bundles in order of
@@ -284,7 +389,7 @@ func (sv *semverTemplate) generateChannels(semverChannels *bundleVersions) []dec
284389 return outChannels
285390}
286391
287- func (sv * semverTemplate ) linkChannels (unlinkedChannels map [string ]* declcfg.Channel , harvestedVersions * bundleVersions ) []declcfg.Channel {
392+ func (sv * SemverTemplateData ) linkChannels (unlinkedChannels map [string ]* declcfg.Channel , harvestedVersions * bundleVersions ) []declcfg.Channel {
288393 // bundle --> version lookup
289394 bundleVersions := make (map [string ]semver.Version )
290395 for _ , vs := range * harvestedVersions {
0 commit comments