1
1
package applier
2
2
3
3
import (
4
+ "cmp"
4
5
"context"
5
6
"crypto/sha256"
6
7
"encoding/hex"
@@ -9,7 +10,6 @@ import (
9
10
"io/fs"
10
11
"maps"
11
12
"slices"
12
- "sort"
13
13
14
14
"github.com/davecgh/go-spew/spew"
15
15
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -27,40 +27,37 @@ import (
27
27
28
28
const (
29
29
revisionHashAnnotation = "olm.operatorframework.io/hash"
30
- revisionHistoryLimit = 5
30
+ // revisionHistoryLimit = 5
31
31
)
32
32
33
- type Boxcutter struct {
34
- Client client.Client
35
- Scheme * runtime.Scheme
36
- BundleRenderer render.BundleRenderer
33
+ type BundleRenderer interface {
34
+ Render (bundleFS fs.FS , ext * ocv1.ClusterExtension ) ([]client.Object , error )
37
35
}
38
36
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
37
+ type RegistryV1BundleRenderer struct {
38
+ BundleRenderer render.BundleRenderer
46
39
}
47
40
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 ()
41
+ func (r * RegistryV1BundleRenderer ) Render (bundleFS fs.FS , ext * ocv1.ClusterExtension ) ([]client.Object , error ) {
42
+ reg , err := source .FromFS (bundleFS ).GetBundle ()
54
43
if err != nil {
55
44
return nil , err
56
45
}
57
-
58
46
watchNamespace , err := GetWatchNamespace (ext )
59
47
if err != nil {
60
48
return nil , err
61
49
}
50
+ return r .BundleRenderer .Render (reg , ext .Spec .Namespace , render .WithTargetNamespaces (watchNamespace ))
51
+ }
52
+
53
+ type BundleRevisionBuilder struct {
54
+ Scheme * runtime.Scheme
55
+ BundleRenderer BundleRenderer
56
+ }
62
57
63
- plain , err := bc .BundleRenderer .Render (reg , ext .Spec .Namespace , render .WithTargetNamespaces (watchNamespace ))
58
+ func (r * BundleRevisionBuilder ) MakeClusterExtensionRevision (bundleFS fs.FS , ext * ocv1.ClusterExtension , objectLabels map [string ]string ) (* ocv1.ClusterExtensionRevision , error ) {
59
+ // extract plain manifests
60
+ plain , err := r .BundleRenderer .Render (bundleFS , ext )
64
61
if err != nil {
65
62
return nil , err
66
63
}
@@ -72,7 +69,7 @@ func (bc *Boxcutter) apply(
72
69
maps .Copy (labels , objectLabels )
73
70
obj .SetLabels (labels )
74
71
75
- gvk , err := apiutil .GVKForObject (obj , bc .Scheme )
72
+ gvk , err := apiutil .GVKForObject (obj , r .Scheme )
76
73
if err != nil {
77
74
return nil , err
78
75
}
@@ -89,18 +86,8 @@ func (bc *Boxcutter) apply(
89
86
})
90
87
}
91
88
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
-
102
89
// Build desired revision
103
- desiredRevision := & ocv1.ClusterExtensionRevision {
90
+ return & ocv1.ClusterExtensionRevision {
104
91
ObjectMeta : metav1.ObjectMeta {
105
92
Annotations : map [string ]string {},
106
93
Labels : map [string ]string {
@@ -116,32 +103,54 @@ func (bc *Boxcutter) apply(
116
103
},
117
104
},
118
105
},
106
+ }, nil
107
+ }
108
+
109
+ type Boxcutter struct {
110
+ Client client.Client
111
+ Scheme * runtime.Scheme
112
+ BundleRevisionBuilder BundleRevisionBuilder
113
+ }
114
+
115
+ func (bc * Boxcutter ) Apply (ctx context.Context , contentFS fs.FS , ext * ocv1.ClusterExtension , objectLabels , storageLabels map [string ]string ) ([]client.Object , string , error ) {
116
+ objs , err := bc .apply (ctx , contentFS , ext , objectLabels , storageLabels )
117
+ return objs , "" , err
118
+ }
119
+
120
+ func (bc * Boxcutter ) apply (ctx context.Context , contentFS fs.FS , ext * ocv1.ClusterExtension , objectLabels , _ map [string ]string ) ([]client.Object , error ) {
121
+ // Generate desired revision
122
+ desiredRevision , err := bc .BundleRevisionBuilder .MakeClusterExtensionRevision (contentFS , ext , objectLabels )
123
+ if err != nil {
124
+ return nil , err
125
+ }
126
+
127
+ // List all existing revisions
128
+ existingRevisions , err := bc .getExistingRevisions (ctx , ext .GetName ())
129
+ if err != nil {
130
+ return nil , err
119
131
}
120
132
desiredHash := computeSHA256Hash (desiredRevision .Spec .Phases )
121
133
122
134
// Sort into current and previous revisions.
123
135
var (
124
136
currentRevision * ocv1.ClusterExtensionRevision
125
- prevRevisions []ocv1.ClusterExtensionRevision
137
+ // prevRevisions []ocv1.ClusterExtensionRevision
126
138
)
127
139
if len (existingRevisions ) > 0 {
128
140
maybeCurrentRevision := existingRevisions [len (existingRevisions )- 1 ]
129
-
130
141
annotations := maybeCurrentRevision .GetAnnotations ()
131
142
if annotations != nil {
132
- if hash , ok := annotations [revisionHashAnnotation ]; ok &&
133
- hash == desiredHash {
143
+ if revisionHash , ok := annotations [revisionHashAnnotation ]; ok && revisionHash == desiredHash {
134
144
currentRevision = & maybeCurrentRevision
135
- prevRevisions = existingRevisions [0 : len (existingRevisions )- 1 ] // previous is everything excluding current
145
+ // prevRevisions = existingRevisions[0 : len(existingRevisions)-1] // previous is everything excluding current
136
146
}
137
147
}
138
148
}
139
149
140
150
if currentRevision == nil {
141
151
// all Revisions are outdated => create a new one.
142
- prevRevisions = existingRevisions
143
- revisionNumber := latestRevisionNumber (prevRevisions )
144
- revisionNumber ++
152
+ prevRevisions := existingRevisions
153
+ revisionNumber := latestRevisionNumber (prevRevisions ) + 1
145
154
146
155
newRevision := desiredRevision
147
156
newRevision .Name = fmt .Sprintf ("%s-%d" , ext .Name , revisionNumber )
@@ -163,25 +172,46 @@ func (bc *Boxcutter) apply(
163
172
}
164
173
165
174
// Delete archived previous revisions over revisionHistory limit
166
- numToDelete := len (prevRevisions ) - revisionHistoryLimit
167
- slices .Reverse (prevRevisions )
175
+ //numToDelete := len(prevRevisions) - revisionHistoryLimit
176
+ //slices.Reverse(prevRevisions)
177
+ //
178
+ //for _, prevRev := range prevRevisions {
179
+ // if numToDelete <= 0 {
180
+ // break
181
+ // }
182
+ //
183
+ // if err := client.IgnoreNotFound(bc.Client.Delete(ctx, &prevRev)); err != nil {
184
+ // return nil, fmt.Errorf("failed to delete revision (history limit): %w", err)
185
+ // }
186
+ // numToDelete--
187
+ //}
168
188
169
- for _ , prevRev := range prevRevisions {
170
- if numToDelete <= 0 {
171
- break
172
- }
189
+ // TODO: Read status from revision.
173
190
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 )
191
+ // Collect objects
192
+ var plain []client.Object
193
+ for _ , phase := range desiredRevision .Spec .Phases {
194
+ for _ , phaseObject := range phase .Objects {
195
+ plain = append (plain , & phaseObject .Object )
176
196
}
177
- numToDelete --
178
197
}
179
-
180
- // TODO: Read status from revision.
181
-
182
198
return plain , nil
183
199
}
184
200
201
+ // getExistingRevisions returns the list of ClusterExtensionRevisions for a ClusterExtension with name extName in revision order (oldest to newest)
202
+ func (bc * Boxcutter ) getExistingRevisions (ctx context.Context , extName string ) ([]ocv1.ClusterExtensionRevision , error ) {
203
+ existingRevisionList := & ocv1.ClusterExtensionRevisionList {}
204
+ if err := bc .Client .List (ctx , existingRevisionList , client.MatchingLabels {
205
+ controllers .ClusterExtensionRevisionOwnerLabel : extName ,
206
+ }); err != nil {
207
+ return nil , fmt .Errorf ("listing revisions: %w" , err )
208
+ }
209
+ slices .SortFunc (existingRevisionList .Items , func (a , b ocv1.ClusterExtensionRevision ) int {
210
+ return cmp .Compare (a .Spec .Revision , b .Spec .Revision )
211
+ })
212
+ return existingRevisionList .Items , nil
213
+ }
214
+
185
215
// computeSHA256Hash returns a sha236 hash value calculated from object.
186
216
func computeSHA256Hash (obj any ) string {
187
217
hasher := sha256 .New ()
@@ -206,21 +236,9 @@ func deepHashObject(hasher hash.Hash, objectToWrite any) {
206
236
}
207
237
}
208
238
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
-
220
239
func latestRevisionNumber (prevRevisions []ocv1.ClusterExtensionRevision ) int64 {
221
240
if len (prevRevisions ) == 0 {
222
241
return 0
223
242
}
224
-
225
243
return prevRevisions [len (prevRevisions )- 1 ].Spec .Revision
226
244
}
0 commit comments