Skip to content

Commit 9401d71

Browse files
author
Per Goncalves da Silva
committed
Progress
Signed-off-by: Per Goncalves da Silva <[email protected]>
1 parent 7a68084 commit 9401d71

File tree

7 files changed

+203
-110
lines changed

7 files changed

+203
-110
lines changed

config/base/operator-controller/rbac/experimental/role.yaml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ rules:
2727
- apiGroups:
2828
- olm.operatorframework.io
2929
resources:
30-
- clusterextensions
30+
- clusterextensionrevisions
3131
verbs:
32+
- create
33+
- delete
3234
- get
3335
- list
3436
- patch
@@ -37,16 +39,28 @@ rules:
3739
- apiGroups:
3840
- olm.operatorframework.io
3941
resources:
42+
- clusterextensionrevisions/finalizers
4043
- clusterextensions/finalizers
4144
verbs:
4245
- update
4346
- apiGroups:
4447
- olm.operatorframework.io
4548
resources:
49+
- clusterextensionrevisions/status
4650
- clusterextensions/status
4751
verbs:
4852
- patch
4953
- update
54+
- apiGroups:
55+
- olm.operatorframework.io
56+
resources:
57+
- clusterextensions
58+
verbs:
59+
- get
60+
- list
61+
- patch
62+
- update
63+
- watch
5064
- apiGroups:
5165
- rbac.authorization.k8s.io
5266
resources:
Lines changed: 83 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package applier
22

33
import (
4+
"cmp"
45
"context"
56
"crypto/sha256"
67
"encoding/hex"
@@ -9,7 +10,6 @@ import (
910
"io/fs"
1011
"maps"
1112
"slices"
12-
"sort"
1313

1414
"github.com/davecgh/go-spew/spew"
1515
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -27,40 +27,37 @@ import (
2727

2828
const (
2929
revisionHashAnnotation = "olm.operatorframework.io/hash"
30-
revisionHistoryLimit = 5
30+
// revisionHistoryLimit = 5
3131
)
3232

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)
3735
}
3836

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
4639
}
4740

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()
5443
if err != nil {
5544
return nil, err
5645
}
57-
5846
watchNamespace, err := GetWatchNamespace(ext)
5947
if err != nil {
6048
return nil, err
6149
}
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+
}
6257

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)
6461
if err != nil {
6562
return nil, err
6663
}
@@ -72,7 +69,7 @@ func (bc *Boxcutter) apply(
7269
maps.Copy(labels, objectLabels)
7370
obj.SetLabels(labels)
7471

75-
gvk, err := apiutil.GVKForObject(obj, bc.Scheme)
72+
gvk, err := apiutil.GVKForObject(obj, r.Scheme)
7673
if err != nil {
7774
return nil, err
7875
}
@@ -89,18 +86,8 @@ func (bc *Boxcutter) apply(
8986
})
9087
}
9188

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-
10289
// Build desired revision
103-
desiredRevision := &ocv1.ClusterExtensionRevision{
90+
return &ocv1.ClusterExtensionRevision{
10491
ObjectMeta: metav1.ObjectMeta{
10592
Annotations: map[string]string{},
10693
Labels: map[string]string{
@@ -116,32 +103,54 @@ func (bc *Boxcutter) apply(
116103
},
117104
},
118105
},
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
119131
}
120132
desiredHash := computeSHA256Hash(desiredRevision.Spec.Phases)
121133

122134
// Sort into current and previous revisions.
123135
var (
124136
currentRevision *ocv1.ClusterExtensionRevision
125-
prevRevisions []ocv1.ClusterExtensionRevision
137+
// prevRevisions []ocv1.ClusterExtensionRevision
126138
)
127139
if len(existingRevisions) > 0 {
128140
maybeCurrentRevision := existingRevisions[len(existingRevisions)-1]
129-
130141
annotations := maybeCurrentRevision.GetAnnotations()
131142
if annotations != nil {
132-
if hash, ok := annotations[revisionHashAnnotation]; ok &&
133-
hash == desiredHash {
143+
if revisionHash, ok := annotations[revisionHashAnnotation]; ok && revisionHash == desiredHash {
134144
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
136146
}
137147
}
138148
}
139149

140150
if currentRevision == nil {
141151
// 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
145154

146155
newRevision := desiredRevision
147156
newRevision.Name = fmt.Sprintf("%s-%d", ext.Name, revisionNumber)
@@ -163,25 +172,46 @@ func (bc *Boxcutter) apply(
163172
}
164173

165174
// 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+
//}
168188

169-
for _, prevRev := range prevRevisions {
170-
if numToDelete <= 0 {
171-
break
172-
}
189+
// TODO: Read status from revision.
173190

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)
176196
}
177-
numToDelete--
178197
}
179-
180-
// TODO: Read status from revision.
181-
182198
return plain, nil
183199
}
184200

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+
185215
// computeSHA256Hash returns a sha236 hash value calculated from object.
186216
func computeSHA256Hash(obj any) string {
187217
hasher := sha256.New()
@@ -206,21 +236,9 @@ func deepHashObject(hasher hash.Hash, objectToWrite any) {
206236
}
207237
}
208238

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-
220239
func latestRevisionNumber(prevRevisions []ocv1.ClusterExtensionRevision) int64 {
221240
if len(prevRevisions) == 0 {
222241
return 0
223242
}
224-
225243
return prevRevisions[len(prevRevisions)-1].Spec.Revision
226244
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package applier_test
2+
3+
//func Test_RegistryV1BundleRenderer_Render_Success(t *testing.T) {
4+
// bundleFS := testutils.NewBundleFS()
5+
//}

internal/operator-controller/rukpak/bundle/source/source_test.go

Lines changed: 6 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,11 @@ import (
1010

1111
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/bundle"
1212
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/bundle/source"
13+
. "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util/testing"
1314
)
1415

1516
const (
1617
olmProperties = "olm.properties"
17-
18-
bundlePathAnnotations = "metadata/annotations.yaml"
19-
bundlePathProperties = "metadata/properties.yaml"
20-
bundlePathCSV = "manifests/csv.yaml"
2118
)
2219

2320
func Test_FromBundle_Success(t *testing.T) {
@@ -30,7 +27,7 @@ func Test_FromBundle_Success(t *testing.T) {
3027
}
3128

3229
func Test_FromFS_Success(t *testing.T) {
33-
rv1, err := source.FromFS(newBundleFS()).GetBundle()
30+
rv1, err := source.FromFS(NewBundleFS()).GetBundle()
3431
require.NoError(t, err)
3532

3633
t.Log("Check package name is correctly taken from metadata/annotations.yaml")
@@ -47,16 +44,16 @@ func Test_FromFS_Fails(t *testing.T) {
4744
}{
4845
{
4946
name: "bundle missing ClusterServiceVersion manifest",
50-
FS: removePaths(newBundleFS(), bundlePathCSV),
47+
FS: removePaths(NewBundleFS(), BundlePathCSV),
5148
}, {
5249
name: "bundle missing metadata/annotations.yaml",
53-
FS: removePaths(newBundleFS(), bundlePathAnnotations),
50+
FS: removePaths(NewBundleFS(), BundlePathAnnotations),
5451
}, {
5552
name: "bundle missing metadata/ directory",
56-
FS: removePaths(newBundleFS(), "metadata/"),
53+
FS: removePaths(NewBundleFS(), "metadata/"),
5754
}, {
5855
name: "bundle missing manifests/ directory",
59-
FS: removePaths(newBundleFS(), "manifests/"),
56+
FS: removePaths(NewBundleFS(), "manifests/"),
6057
},
6158
} {
6259
t.Run(tt.name, func(t *testing.T) {
@@ -66,39 +63,6 @@ func Test_FromFS_Fails(t *testing.T) {
6663
}
6764
}
6865

69-
func newBundleFS() fstest.MapFS {
70-
annotationsYml := `
71-
annotations:
72-
operators.operatorframework.io.bundle.mediatype.v1: registry+v1
73-
operators.operatorframework.io.bundle.package.v1: test
74-
`
75-
76-
propertiesYml := `
77-
properties:
78-
- type: "from-file-key"
79-
value: "from-file-value"
80-
`
81-
82-
csvYml := `
83-
apiVersion: operators.operatorframework.io/v1alpha1
84-
kind: ClusterServiceVersion
85-
metadata:
86-
name: test.v1.0.0
87-
annotations:
88-
olm.properties: '[{"type":"from-csv-annotations-key", "value":"from-csv-annotations-value"}]'
89-
spec:
90-
installModes:
91-
- type: AllNamespaces
92-
supported: true
93-
`
94-
95-
return fstest.MapFS{
96-
bundlePathAnnotations: &fstest.MapFile{Data: []byte(strings.Trim(annotationsYml, "\n"))},
97-
bundlePathProperties: &fstest.MapFile{Data: []byte(strings.Trim(propertiesYml, "\n"))},
98-
bundlePathCSV: &fstest.MapFile{Data: []byte(strings.Trim(csvYml, "\n"))},
99-
}
100-
}
101-
10266
func removePaths(mapFs fstest.MapFS, paths ...string) fstest.MapFS {
10367
for k := range mapFs {
10468
for _, path := range paths {

0 commit comments

Comments
 (0)