Skip to content

Commit f796c03

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

File tree

7 files changed

+208
-111
lines changed

7 files changed

+208
-111
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 & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
11
package applier
22

33
import (
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

2827
const (
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.
186215
func 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-
220238
func latestRevisionNumber(prevRevisions []ocv1.ClusterExtensionRevision) int64 {
221239
if len(prevRevisions) == 0 {
222240
return 0
223241
}
224-
225242
return prevRevisions[len(prevRevisions)-1].Spec.Revision
226243
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package applier_test
2+
3+
import (
4+
"testing"
5+
6+
testutils "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util/testing"
7+
)
8+
9+
func Test_RegistryV1BundleRenderer_Render_Success(t *testing.T) {
10+
bundleFS := testutils.NewBundleFS()
11+
}

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)