11package applier
22
33import (
4+ "cmp"
45 "context"
56 "crypto/sha256"
67 "encoding/hex"
78 "fmt"
9+ "github.com/davecgh/go-spew/spew"
810 "hash"
911 "io/fs"
10- "maps"
11- "slices"
12- "sort"
13-
14- "github.com/davecgh/go-spew/spew"
1512 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1613 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1714 "k8s.io/apimachinery/pkg/runtime"
15+ "maps"
1816 "sigs.k8s.io/controller-runtime/pkg/client"
1917 "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
2018 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
19+ "slices"
2120
2221 ocv1 "github.com/operator-framework/operator-controller/api/v1"
2322 "github.com/operator-framework/operator-controller/internal/operator-controller/controllers"
@@ -27,40 +26,37 @@ import (
2726
2827const (
2928 revisionHashAnnotation = "olm.operatorframework.io/hash"
30- revisionHistoryLimit = 5
29+ // revisionHistoryLimit = 5
3130)
3231
33- type Boxcutter struct {
34- Client client.Client
35- Scheme * runtime.Scheme
36- BundleRenderer render.BundleRenderer
32+ type BundleRenderer interface {
33+ Render (bundleFS fs.FS , ext * ocv1.ClusterExtension ) ([]client.Object , error )
3734}
3835
39- func (bc * Boxcutter ) Apply (
40- ctx context.Context , contentFS fs.FS ,
41- ext * ocv1.ClusterExtension ,
42- objectLabels , storageLabels map [string ]string ,
43- ) ([]client.Object , string , error ) {
44- objs , err := bc .apply (ctx , contentFS , ext , objectLabels , storageLabels )
45- return objs , "" , err
36+ type RegistryV1BundleRenderer struct {
37+ BundleRenderer render.BundleRenderer
4638}
4739
48- func (bc * Boxcutter ) apply (
49- ctx context.Context , contentFS fs.FS ,
50- ext * ocv1.ClusterExtension ,
51- objectLabels , _ map [string ]string ,
52- ) ([]client.Object , error ) {
53- reg , err := source .FromFS (contentFS ).GetBundle ()
40+ func (r * RegistryV1BundleRenderer ) Render (bundleFS fs.FS , ext * ocv1.ClusterExtension ) ([]client.Object , error ) {
41+ reg , err := source .FromFS (bundleFS ).GetBundle ()
5442 if err != nil {
5543 return nil , err
5644 }
57-
5845 watchNamespace , err := GetWatchNamespace (ext )
5946 if err != nil {
6047 return nil , err
6148 }
49+ return r .BundleRenderer .Render (reg , ext .Spec .Namespace , render .WithTargetNamespaces (watchNamespace ))
50+ }
6251
63- plain , err := bc .BundleRenderer .Render (reg , ext .Spec .Namespace , render .WithTargetNamespaces (watchNamespace ))
52+ type BundleRevisionBuilder struct {
53+ Scheme * runtime.Scheme
54+ BundleRenderer BundleRenderer
55+ }
56+
57+ func (r * BundleRevisionBuilder ) MakeClusterExtensionRevision (bundleFS fs.FS , ext * ocv1.ClusterExtension , objectLabels map [string ]string ) (* ocv1.ClusterExtensionRevision , error ) {
58+ // extract plain manifests
59+ plain , err := r .BundleRenderer .Render (bundleFS , ext )
6460 if err != nil {
6561 return nil , err
6662 }
@@ -72,7 +68,7 @@ func (bc *Boxcutter) apply(
7268 maps .Copy (labels , objectLabels )
7369 obj .SetLabels (labels )
7470
75- gvk , err := apiutil .GVKForObject (obj , bc .Scheme )
71+ gvk , err := apiutil .GVKForObject (obj , r .Scheme )
7672 if err != nil {
7773 return nil , err
7874 }
@@ -89,18 +85,8 @@ func (bc *Boxcutter) apply(
8985 })
9086 }
9187
92- // List all existing revisions
93- existingRevisionList := & ocv1.ClusterExtensionRevisionList {}
94- if err := bc .Client .List (ctx , existingRevisionList , client.MatchingLabels {
95- controllers .ClusterExtensionRevisionOwnerLabel : ext .Name ,
96- }); err != nil {
97- return nil , fmt .Errorf ("listing revisions: %w" , err )
98- }
99- sort .Sort (revisionAscending (existingRevisionList .Items ))
100- existingRevisions := existingRevisionList .Items
101-
10288 // Build desired revision
103- desiredRevision := & ocv1.ClusterExtensionRevision {
89+ return & ocv1.ClusterExtensionRevision {
10490 ObjectMeta : metav1.ObjectMeta {
10591 Annotations : map [string ]string {},
10692 Labels : map [string ]string {
@@ -116,6 +102,31 @@ func (bc *Boxcutter) apply(
116102 },
117103 },
118104 },
105+ }, nil
106+ }
107+
108+ type Boxcutter struct {
109+ Client client.Client
110+ Scheme * runtime.Scheme
111+ BundleRevisionBuilder BundleRevisionBuilder
112+ }
113+
114+ func (bc * Boxcutter ) Apply (ctx context.Context , contentFS fs.FS , ext * ocv1.ClusterExtension , objectLabels , storageLabels map [string ]string ) ([]client.Object , string , error ) {
115+ objs , err := bc .apply (ctx , contentFS , ext , objectLabels , storageLabels )
116+ return objs , "" , err
117+ }
118+
119+ func (bc * Boxcutter ) apply (ctx context.Context , contentFS fs.FS , ext * ocv1.ClusterExtension , objectLabels , _ map [string ]string ) ([]client.Object , error ) {
120+ // Generate desired revision
121+ desiredRevision , err := bc .BundleRevisionBuilder .MakeClusterExtensionRevision (contentFS , ext , objectLabels )
122+ if err != nil {
123+ return nil , err
124+ }
125+
126+ // List all existing revisions
127+ existingRevisions , err := bc .getExistingRevisions (ctx , ext .GetName ())
128+ if err != nil {
129+ return nil , err
119130 }
120131 desiredHash := computeSHA256Hash (desiredRevision .Spec .Phases )
121132
@@ -126,11 +137,9 @@ func (bc *Boxcutter) apply(
126137 )
127138 if len (existingRevisions ) > 0 {
128139 maybeCurrentRevision := existingRevisions [len (existingRevisions )- 1 ]
129-
130140 annotations := maybeCurrentRevision .GetAnnotations ()
131141 if annotations != nil {
132- if hash , ok := annotations [revisionHashAnnotation ]; ok &&
133- hash == desiredHash {
142+ if revisionHash , ok := annotations [revisionHashAnnotation ]; ok && revisionHash == desiredHash {
134143 currentRevision = & maybeCurrentRevision
135144 prevRevisions = existingRevisions [0 : len (existingRevisions )- 1 ] // previous is everything excluding current
136145 }
@@ -140,8 +149,7 @@ func (bc *Boxcutter) apply(
140149 if currentRevision == nil {
141150 // all Revisions are outdated => create a new one.
142151 prevRevisions = existingRevisions
143- revisionNumber := latestRevisionNumber (prevRevisions )
144- revisionNumber ++
152+ revisionNumber := latestRevisionNumber (prevRevisions ) + 1
145153
146154 newRevision := desiredRevision
147155 newRevision .Name = fmt .Sprintf ("%s-%d" , ext .Name , revisionNumber )
@@ -163,25 +171,46 @@ func (bc *Boxcutter) apply(
163171 }
164172
165173 // Delete archived previous revisions over revisionHistory limit
166- numToDelete := len (prevRevisions ) - revisionHistoryLimit
167- slices .Reverse (prevRevisions )
174+ //numToDelete := len(prevRevisions) - revisionHistoryLimit
175+ //slices.Reverse(prevRevisions)
176+ //
177+ //for _, prevRev := range prevRevisions {
178+ // if numToDelete <= 0 {
179+ // break
180+ // }
181+ //
182+ // if err := client.IgnoreNotFound(bc.Client.Delete(ctx, &prevRev)); err != nil {
183+ // return nil, fmt.Errorf("failed to delete revision (history limit): %w", err)
184+ // }
185+ // numToDelete--
186+ //}
168187
169- for _ , prevRev := range prevRevisions {
170- if numToDelete <= 0 {
171- break
172- }
188+ // TODO: Read status from revision.
173189
174- if err := client .IgnoreNotFound (bc .Client .Delete (ctx , & prevRev )); err != nil {
175- return nil , fmt .Errorf ("failed to delete revision (history limit): %w" , err )
190+ // Collect objects
191+ var plain []client.Object
192+ for _ , phase := range desiredRevision .Spec .Phases {
193+ for _ , phaseObject := range phase .Objects {
194+ plain = append (plain , & phaseObject .Object )
176195 }
177- numToDelete --
178196 }
179-
180- // TODO: Read status from revision.
181-
182197 return plain , nil
183198}
184199
200+ // getExistingRevisions returns the list of ClusterExtensionRevisions for a ClusterExtension with name extName in revision order (oldest to newest)
201+ func (bc * Boxcutter ) getExistingRevisions (ctx context.Context , extName string ) ([]ocv1.ClusterExtensionRevision , error ) {
202+ existingRevisionList := & ocv1.ClusterExtensionRevisionList {}
203+ if err := bc .Client .List (ctx , existingRevisionList , client.MatchingLabels {
204+ controllers .ClusterExtensionRevisionOwnerLabel : extName ,
205+ }); err != nil {
206+ return nil , fmt .Errorf ("listing revisions: %w" , err )
207+ }
208+ slices .SortFunc (existingRevisionList .Items , func (a , b ocv1.ClusterExtensionRevision ) int {
209+ return cmp .Compare (a .Spec .Revision , b .Spec .Revision )
210+ })
211+ return existingRevisionList .Items , nil
212+ }
213+
185214// computeSHA256Hash returns a sha236 hash value calculated from object.
186215func computeSHA256Hash (obj any ) string {
187216 hasher := sha256 .New ()
@@ -206,21 +235,9 @@ func deepHashObject(hasher hash.Hash, objectToWrite any) {
206235 }
207236}
208237
209- type revisionAscending []ocv1.ClusterExtensionRevision
210-
211- func (a revisionAscending ) Len () int { return len (a ) }
212- func (a revisionAscending ) Swap (i , j int ) { a [i ], a [j ] = a [j ], a [i ] }
213- func (a revisionAscending ) Less (i , j int ) bool {
214- iObj := a [i ]
215- jObj := a [j ]
216-
217- return iObj .Spec .Revision < jObj .Spec .Revision
218- }
219-
220238func latestRevisionNumber (prevRevisions []ocv1.ClusterExtensionRevision ) int64 {
221239 if len (prevRevisions ) == 0 {
222240 return 0
223241 }
224-
225242 return prevRevisions [len (prevRevisions )- 1 ].Spec .Revision
226243}
0 commit comments