@@ -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
2524func (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
4137type ListPackagesResult struct {
42- Packages []model.Package
38+ Packages []declcfg.Package
39+ Channels []declcfg.Channel
40+ Bundles []declcfg.Bundle
4341}
4442
4543func (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+
74134type ListChannels struct {
75135 IndexReference string
76136 PackageName string
77137 Registry image.Registry
78138}
79139
80140func (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
107160type ListChannelsResult struct {
108- Channels []model .Channel
161+ Channels []declcfg .Channel
109162}
110163
111164func (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
137190func (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
169206type ListBundlesResult struct {
170- Bundles []model.Bundle
207+ Bundles []declcfg.Bundle
208+ Channels []declcfg.Channel
171209}
172210
173211func (r * ListBundlesResult ) WriteColumns (w io.Writer ) error {
174212 tw := tabwriter .NewWriter (w , 0 , 4 , 2 , ' ' , 0 )
175213 if _ , err := fmt .Fprintln (tw , "PACKAGE\t CHANNEL\t BUNDLE\t REPLACES\t SKIPS\t SKIP RANGE\t IMAGE" ); 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