1
1
package applier
2
2
3
3
import (
4
+ "cmp"
4
5
"context"
5
6
"crypto/sha256"
6
7
"encoding/hex"
7
8
"fmt"
9
+ "github.com/davecgh/go-spew/spew"
8
10
"hash"
9
11
"io/fs"
10
- "maps"
11
- "slices"
12
- "sort"
13
-
14
- "github.com/davecgh/go-spew/spew"
15
12
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
16
13
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
17
14
"k8s.io/apimachinery/pkg/runtime"
15
+ "maps"
18
16
"sigs.k8s.io/controller-runtime/pkg/client"
19
17
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
20
18
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
19
+ "slices"
21
20
22
21
ocv1 "github.com/operator-framework/operator-controller/api/v1"
23
22
"github.com/operator-framework/operator-controller/internal/operator-controller/controllers"
@@ -27,40 +26,37 @@ import (
27
26
28
27
const (
29
28
revisionHashAnnotation = "olm.operatorframework.io/hash"
30
- revisionHistoryLimit = 5
29
+ // revisionHistoryLimit = 5
31
30
)
32
31
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 )
37
34
}
38
35
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
46
38
}
47
39
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 ()
54
42
if err != nil {
55
43
return nil , err
56
44
}
57
-
58
45
watchNamespace , err := GetWatchNamespace (ext )
59
46
if err != nil {
60
47
return nil , err
61
48
}
49
+ return r .BundleRenderer .Render (reg , ext .Spec .Namespace , render .WithTargetNamespaces (watchNamespace ))
50
+ }
62
51
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 )
64
60
if err != nil {
65
61
return nil , err
66
62
}
@@ -72,7 +68,7 @@ func (bc *Boxcutter) apply(
72
68
maps .Copy (labels , objectLabels )
73
69
obj .SetLabels (labels )
74
70
75
- gvk , err := apiutil .GVKForObject (obj , bc .Scheme )
71
+ gvk , err := apiutil .GVKForObject (obj , r .Scheme )
76
72
if err != nil {
77
73
return nil , err
78
74
}
@@ -89,18 +85,8 @@ func (bc *Boxcutter) apply(
89
85
})
90
86
}
91
87
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
88
// Build desired revision
103
- desiredRevision := & ocv1.ClusterExtensionRevision {
89
+ return & ocv1.ClusterExtensionRevision {
104
90
ObjectMeta : metav1.ObjectMeta {
105
91
Annotations : map [string ]string {},
106
92
Labels : map [string ]string {
@@ -116,6 +102,31 @@ func (bc *Boxcutter) apply(
116
102
},
117
103
},
118
104
},
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
119
130
}
120
131
desiredHash := computeSHA256Hash (desiredRevision .Spec .Phases )
121
132
@@ -126,11 +137,9 @@ func (bc *Boxcutter) apply(
126
137
)
127
138
if len (existingRevisions ) > 0 {
128
139
maybeCurrentRevision := existingRevisions [len (existingRevisions )- 1 ]
129
-
130
140
annotations := maybeCurrentRevision .GetAnnotations ()
131
141
if annotations != nil {
132
- if hash , ok := annotations [revisionHashAnnotation ]; ok &&
133
- hash == desiredHash {
142
+ if revisionHash , ok := annotations [revisionHashAnnotation ]; ok && revisionHash == desiredHash {
134
143
currentRevision = & maybeCurrentRevision
135
144
prevRevisions = existingRevisions [0 : len (existingRevisions )- 1 ] // previous is everything excluding current
136
145
}
@@ -140,8 +149,7 @@ func (bc *Boxcutter) apply(
140
149
if currentRevision == nil {
141
150
// all Revisions are outdated => create a new one.
142
151
prevRevisions = existingRevisions
143
- revisionNumber := latestRevisionNumber (prevRevisions )
144
- revisionNumber ++
152
+ revisionNumber := latestRevisionNumber (prevRevisions ) + 1
145
153
146
154
newRevision := desiredRevision
147
155
newRevision .Name = fmt .Sprintf ("%s-%d" , ext .Name , revisionNumber )
@@ -163,25 +171,46 @@ func (bc *Boxcutter) apply(
163
171
}
164
172
165
173
// 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
+ //}
168
187
169
- for _ , prevRev := range prevRevisions {
170
- if numToDelete <= 0 {
171
- break
172
- }
188
+ // TODO: Read status from revision.
173
189
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 )
176
195
}
177
- numToDelete --
178
196
}
179
-
180
- // TODO: Read status from revision.
181
-
182
197
return plain , nil
183
198
}
184
199
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
+
185
214
// computeSHA256Hash returns a sha236 hash value calculated from object.
186
215
func computeSHA256Hash (obj any ) string {
187
216
hasher := sha256 .New ()
@@ -206,21 +235,9 @@ func deepHashObject(hasher hash.Hash, objectToWrite any) {
206
235
}
207
236
}
208
237
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
238
func latestRevisionNumber (prevRevisions []ocv1.ClusterExtensionRevision ) int64 {
221
239
if len (prevRevisions ) == 0 {
222
240
return 0
223
241
}
224
-
225
242
return prevRevisions [len (prevRevisions )- 1 ].Spec .Revision
226
243
}
0 commit comments