Skip to content

Commit 1497b3d

Browse files
committed
relocate all model functionality to declcfg and eliminate model package
Signed-off-by: grokspawn <jordan@nimblewidget.com>
1 parent 9933bd6 commit 1497b3d

27 files changed

Lines changed: 949 additions & 3750 deletions

alpha/action/list.go

Lines changed: 181 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"github.com/operator-framework/api/pkg/operators/v1alpha1"
1414

1515
"github.com/operator-framework/operator-registry/alpha/declcfg"
16-
"github.com/operator-framework/operator-registry/alpha/model"
1716
"github.com/operator-framework/operator-registry/pkg/image"
1817
)
1918

@@ -23,23 +22,22 @@ type ListPackages struct {
2322
}
2423

2524
func (l *ListPackages) Run(ctx context.Context) (*ListPackagesResult, error) {
26-
m, err := indexRefToModel(ctx, l.IndexReference, l.Registry)
25+
cfg, err := indexRefToDeclcfg(ctx, l.IndexReference, l.Registry)
2726
if err != nil {
2827
return nil, err
2928
}
3029

31-
pkgs := []model.Package{}
32-
for _, pkg := range m {
33-
pkgs = append(pkgs, *pkg)
34-
}
30+
pkgs := cfg.Packages
3531
sort.Slice(pkgs, func(i, j int) bool {
3632
return pkgs[i].Name < pkgs[j].Name
3733
})
38-
return &ListPackagesResult{Packages: pkgs}, nil
34+
return &ListPackagesResult{Packages: pkgs, Channels: cfg.Channels, Bundles: cfg.Bundles}, nil
3935
}
4036

4137
type ListPackagesResult struct {
42-
Packages []model.Package
38+
Packages []declcfg.Package
39+
Channels []declcfg.Channel
40+
Bundles []declcfg.Bundle
4341
}
4442

4543
func (r *ListPackagesResult) WriteColumns(w io.Writer) error {
@@ -48,64 +46,119 @@ func (r *ListPackagesResult) WriteColumns(w io.Writer) error {
4846
return err
4947
}
5048
for _, pkg := range r.Packages {
51-
if _, err := fmt.Fprintf(tw, "%s\t%s\t%s\n", pkg.Name, getDisplayName(pkg), pkg.DefaultChannel.Name); err != nil {
49+
displayName := getDisplayName(pkg, r.Channels, r.Bundles)
50+
if _, err := fmt.Fprintf(tw, "%s\t%s\t%s\n", pkg.Name, displayName, pkg.DefaultChannel); err != nil {
5251
return err
5352
}
5453
}
5554
return tw.Flush()
5655
}
5756

58-
func getDisplayName(pkg model.Package) string {
59-
if pkg.DefaultChannel == nil {
57+
func getDisplayName(pkg declcfg.Package, channels []declcfg.Channel, bundles []declcfg.Bundle) string {
58+
if pkg.DefaultChannel == "" {
59+
return ""
60+
}
61+
62+
// Find the default channel
63+
var defaultCh *declcfg.Channel
64+
for i, ch := range channels {
65+
if ch.Package == pkg.Name && ch.Name == pkg.DefaultChannel {
66+
defaultCh = &channels[i]
67+
break
68+
}
69+
}
70+
if defaultCh == nil {
71+
return ""
72+
}
73+
74+
// Find the head bundle
75+
head, err := findChannelHead(defaultCh.Entries)
76+
if err != nil {
6077
return ""
6178
}
62-
head, err := pkg.DefaultChannel.Head()
63-
if err != nil || head == nil || head.CsvJSON == "" {
79+
80+
// Find the bundle
81+
var headBundle *declcfg.Bundle
82+
for i, b := range bundles {
83+
if b.Package == pkg.Name && b.Name == head {
84+
headBundle = &bundles[i]
85+
break
86+
}
87+
}
88+
if headBundle == nil || headBundle.CsvJSON == "" {
6489
return ""
6590
}
6691

6792
csv := v1alpha1.ClusterServiceVersion{}
68-
if err := json.Unmarshal([]byte(head.CsvJSON), &csv); err != nil {
93+
if err := json.Unmarshal([]byte(headBundle.CsvJSON), &csv); err != nil {
6994
return ""
7095
}
7196
return csv.Spec.DisplayName
7297
}
7398

99+
// findChannelHead finds the head bundle of a channel by analyzing the replaces chain.
100+
func findChannelHead(entries []declcfg.ChannelEntry) (string, error) {
101+
if len(entries) == 0 {
102+
return "", fmt.Errorf("channel has no entries")
103+
}
104+
105+
// Build a map of bundles that are replaced
106+
replaced := make(map[string]bool)
107+
for _, entry := range entries {
108+
if entry.Replaces != "" {
109+
replaced[entry.Replaces] = true
110+
}
111+
for _, skip := range entry.Skips {
112+
replaced[skip] = true
113+
}
114+
}
115+
116+
// Find bundles that are not replaced by anything
117+
var heads []string
118+
for _, entry := range entries {
119+
if !replaced[entry.Name] {
120+
heads = append(heads, entry.Name)
121+
}
122+
}
123+
124+
if len(heads) == 0 {
125+
return "", fmt.Errorf("channel has circular replaces chain, no head found")
126+
}
127+
if len(heads) > 1 {
128+
return "", fmt.Errorf("channel has multiple heads: %v", heads)
129+
}
130+
131+
return heads[0], nil
132+
}
133+
74134
type ListChannels struct {
75135
IndexReference string
76136
PackageName string
77137
Registry image.Registry
78138
}
79139

80140
func (l *ListChannels) Run(ctx context.Context) (*ListChannelsResult, error) {
81-
m, err := indexRefToModel(ctx, l.IndexReference, l.Registry)
82-
if err != nil {
83-
return nil, err
84-
}
85-
86-
pkgs, err := getPackages(m, l.PackageName)
141+
cfg, err := indexRefToDeclcfg(ctx, l.IndexReference, l.Registry)
87142
if err != nil {
88143
return nil, err
89144
}
90145

91-
channels := []model.Channel{}
92-
for _, pkg := range pkgs {
93-
for _, ch := range pkg.Channels {
94-
channels = append(channels, *ch)
95-
}
146+
channels := filterChannelsByPackage(cfg.Channels, l.PackageName)
147+
if l.PackageName != "" && len(channels) == 0 {
148+
return nil, fmt.Errorf("package %q not found", l.PackageName)
96149
}
97150

98151
sort.Slice(channels, func(i, j int) bool {
99-
if channels[i].Package.Name != channels[j].Package.Name {
100-
return channels[i].Package.Name < channels[j].Package.Name
152+
if channels[i].Package != channels[j].Package {
153+
return channels[i].Package < channels[j].Package
101154
}
102155
return channels[i].Name < channels[j].Name
103156
})
104157
return &ListChannelsResult{Channels: channels}, nil
105158
}
106159

107160
type ListChannelsResult struct {
108-
Channels []model.Channel
161+
Channels []declcfg.Channel
109162
}
110163

111164
func (r *ListChannelsResult) WriteColumns(w io.Writer) error {
@@ -115,13 +168,13 @@ func (r *ListChannelsResult) WriteColumns(w io.Writer) error {
115168
}
116169
for _, ch := range r.Channels {
117170
headStr := ""
118-
head, err := ch.Head()
171+
head, err := findChannelHead(ch.Entries)
119172
if err != nil {
120173
headStr = fmt.Sprintf("ERROR: %s", err)
121174
} else {
122-
headStr = head.Name
175+
headStr = head
123176
}
124-
if _, err := fmt.Fprintf(tw, "%s\t%s\t%s\n", ch.Package.Name, ch.Name, headStr); err != nil {
177+
if _, err := fmt.Fprintf(tw, "%s\t%s\t%s\n", ch.Package, ch.Name, headStr); err != nil {
125178
return err
126179
}
127180
}
@@ -135,55 +188,103 @@ type ListBundles struct {
135188
}
136189

137190
func (l *ListBundles) Run(ctx context.Context) (*ListBundlesResult, error) {
138-
m, err := indexRefToModel(ctx, l.IndexReference, l.Registry)
191+
cfg, err := indexRefToDeclcfg(ctx, l.IndexReference, l.Registry)
139192
if err != nil {
140193
return nil, err
141194
}
142195

143-
pkgs, err := getPackages(m, l.PackageName)
144-
if err != nil {
145-
return nil, err
146-
}
196+
bundles := filterBundlesByPackage(cfg.Bundles, cfg.Channels, l.PackageName)
197+
channels := filterChannelsByPackage(cfg.Channels, l.PackageName)
147198

148-
bundles := []model.Bundle{}
149-
for _, pkg := range pkgs {
150-
for _, ch := range pkg.Channels {
151-
for _, b := range ch.Bundles {
152-
bundles = append(bundles, *b)
153-
}
154-
}
199+
if l.PackageName != "" && len(bundles) == 0 {
200+
return nil, fmt.Errorf("package %q not found", l.PackageName)
155201
}
156202

157-
sort.Slice(bundles, func(i, j int) bool {
158-
if bundles[i].Package.Name != bundles[j].Package.Name {
159-
return bundles[i].Package.Name < bundles[j].Package.Name
160-
}
161-
if bundles[i].Channel.Name != bundles[j].Channel.Name {
162-
return bundles[i].Channel.Name < bundles[j].Channel.Name
163-
}
164-
return bundles[i].Name < bundles[j].Name
165-
})
166-
return &ListBundlesResult{Bundles: bundles}, nil
203+
return &ListBundlesResult{Bundles: bundles, Channels: channels}, nil
167204
}
168205

169206
type ListBundlesResult struct {
170-
Bundles []model.Bundle
207+
Bundles []declcfg.Bundle
208+
Channels []declcfg.Channel
171209
}
172210

173211
func (r *ListBundlesResult) WriteColumns(w io.Writer) error {
174212
tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0)
175213
if _, err := fmt.Fprintln(tw, "PACKAGE\tCHANNEL\tBUNDLE\tREPLACES\tSKIPS\tSKIP RANGE\tIMAGE"); err != nil {
176214
return err
177215
}
216+
217+
// Build a map of bundle -> channels containing it
218+
type bundleChannelEntry struct {
219+
channel string
220+
replaces string
221+
skips []string
222+
skipRange string
223+
}
224+
bundleToChannels := make(map[string][]bundleChannelEntry)
225+
for _, ch := range r.Channels {
226+
for _, entry := range ch.Entries {
227+
key := ch.Package + "/" + entry.Name
228+
bundleToChannels[key] = append(bundleToChannels[key], bundleChannelEntry{
229+
channel: ch.Name,
230+
replaces: entry.Replaces,
231+
skips: entry.Skips,
232+
skipRange: entry.SkipRange,
233+
})
234+
}
235+
}
236+
237+
// Build list of bundle instances (one per channel)
238+
type bundleInstance struct {
239+
pkg string
240+
channel string
241+
name string
242+
replaces string
243+
skips []string
244+
skipRange string
245+
image string
246+
}
247+
var instances []bundleInstance
248+
178249
for _, b := range r.Bundles {
179-
if _, err := fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", b.Package.Name, b.Channel.Name, b.Name, b.Replaces, strings.Join(b.Skips, ","), b.SkipRange, b.Image); err != nil {
250+
key := b.Package + "/" + b.Name
251+
channels := bundleToChannels[key]
252+
253+
// Create one entry per channel
254+
for _, ch := range channels {
255+
instances = append(instances, bundleInstance{
256+
pkg: b.Package,
257+
channel: ch.channel,
258+
name: b.Name,
259+
replaces: ch.replaces,
260+
skips: ch.skips,
261+
skipRange: ch.skipRange,
262+
image: b.Image,
263+
})
264+
}
265+
}
266+
267+
// Sort instances
268+
sort.Slice(instances, func(i, j int) bool {
269+
if instances[i].pkg != instances[j].pkg {
270+
return instances[i].pkg < instances[j].pkg
271+
}
272+
if instances[i].channel != instances[j].channel {
273+
return instances[i].channel < instances[j].channel
274+
}
275+
return instances[i].name < instances[j].name
276+
})
277+
278+
// Write instances
279+
for _, inst := range instances {
280+
if _, err := fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", inst.pkg, inst.channel, inst.name, inst.replaces, strings.Join(inst.skips, ","), inst.skipRange, inst.image); err != nil {
180281
return err
181282
}
182283
}
183284
return tw.Flush()
184285
}
185286

186-
func indexRefToModel(ctx context.Context, ref string, reg image.Registry) (model.Model, error) {
287+
func indexRefToDeclcfg(ctx context.Context, ref string, reg image.Registry) (*declcfg.DeclarativeConfig, error) {
187288
render := Render{
188289
Refs: []string{ref},
189290
AllowedRefMask: RefDCImage | RefDCDir,
@@ -197,16 +298,33 @@ func indexRefToModel(ctx context.Context, ref string, reg image.Registry) (model
197298
return nil, err
198299
}
199300

200-
return declcfg.ConvertToModel(*cfg)
301+
return cfg, nil
302+
}
303+
304+
func filterChannelsByPackage(channels []declcfg.Channel, packageName string) []declcfg.Channel {
305+
if packageName == "" {
306+
return channels
307+
}
308+
309+
var result []declcfg.Channel
310+
for _, ch := range channels {
311+
if ch.Package == packageName {
312+
result = append(result, ch)
313+
}
314+
}
315+
return result
201316
}
202317

203-
func getPackages(m model.Model, packageName string) (model.Model, error) {
318+
func filterBundlesByPackage(bundles []declcfg.Bundle, channels []declcfg.Channel, packageName string) []declcfg.Bundle {
204319
if packageName == "" {
205-
return m, nil
320+
return bundles
206321
}
207-
pkg, ok := m[packageName]
208-
if !ok {
209-
return nil, fmt.Errorf("package %q not found", packageName)
322+
323+
var result []declcfg.Bundle
324+
for _, b := range bundles {
325+
if b.Package == packageName {
326+
result = append(result, b)
327+
}
210328
}
211-
return model.Model{packageName: pkg}, nil
329+
return result
212330
}

0 commit comments

Comments
 (0)