@@ -11,18 +11,25 @@ import (
1111 "io/fs"
1212 "maps"
1313 "slices"
14+ "strings"
1415
1516 "github.com/davecgh/go-spew/spew"
17+ "helm.sh/helm/v3/pkg/release"
18+ "helm.sh/helm/v3/pkg/storage/driver"
1619 "k8s.io/apimachinery/pkg/api/meta"
1720 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1821 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1922 "k8s.io/apimachinery/pkg/runtime"
2023 "sigs.k8s.io/controller-runtime/pkg/client"
2124 "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
2225 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
26+ "sigs.k8s.io/yaml"
27+
28+ helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client"
2329
2430 ocv1 "github.com/operator-framework/operator-controller/api/v1"
2531 "github.com/operator-framework/operator-controller/internal/operator-controller/controllers"
32+ "github.com/operator-framework/operator-controller/internal/operator-controller/labels"
2633 "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/bundle/source"
2734 "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render"
2835)
@@ -33,13 +40,64 @@ const (
3340
3441type ClusterExtensionRevisionGenerator interface {
3542 GenerateRevision (bundleFS fs.FS , ext * ocv1.ClusterExtension , objectLabels , revisionAnnotations map [string ]string ) (* ocv1.ClusterExtensionRevision , error )
43+ GenerateRevisionFromHelmRelease (
44+ helmRelease * release.Release , ext * ocv1.ClusterExtension ,
45+ objectLabels map [string ]string ,
46+ ) (* ocv1.ClusterExtensionRevision , error )
3647}
3748
3849type SimpleRevisionGenerator struct {
3950 Scheme * runtime.Scheme
4051 BundleRenderer BundleRenderer
4152}
4253
54+ func (r * SimpleRevisionGenerator ) GenerateRevisionFromHelmRelease (
55+ helmRelease * release.Release , ext * ocv1.ClusterExtension ,
56+ objectLabels map [string ]string ,
57+ ) (* ocv1.ClusterExtensionRevision , error ) {
58+ docs := splitManifestDocuments (helmRelease .Manifest )
59+ objs := make ([]ocv1.ClusterExtensionRevisionObject , 0 , len (docs ))
60+ for _ , doc := range docs {
61+ obj := unstructured.Unstructured {}
62+ if err := yaml .Unmarshal ([]byte (doc ), & obj ); err != nil {
63+ return nil , err
64+ }
65+
66+ labels := maps .Clone (obj .GetLabels ())
67+ if labels == nil {
68+ labels = map [string ]string {}
69+ }
70+ maps .Copy (labels , objectLabels )
71+ obj .SetLabels (labels )
72+ obj .SetOwnerReferences (nil ) // reset OwnerReferences for migration.
73+
74+ objs = append (objs , ocv1.ClusterExtensionRevisionObject {
75+ Object : obj ,
76+ CollisionProtection : ocv1 .CollisionProtectionNone , // allow to adopt objects from previous release
77+ })
78+ }
79+
80+ // Build desired revision
81+ return & ocv1.ClusterExtensionRevision {
82+ ObjectMeta : metav1.ObjectMeta {
83+ Annotations : map [string ]string {
84+ labels .BundleNameKey : helmRelease .Labels [labels .BundleNameKey ],
85+ labels .PackageNameKey : helmRelease .Labels [labels .PackageNameKey ],
86+ labels .BundleVersionKey : helmRelease .Labels [labels .BundleVersionKey ],
87+ labels .BundleReferenceKey : helmRelease .Labels [labels .BundleReferenceKey ],
88+ },
89+ Labels : map [string ]string {
90+ controllers .ClusterExtensionRevisionOwnerLabel : ext .Name ,
91+ },
92+ Name : fmt .Sprintf ("%s-1" , ext .Name ),
93+ },
94+ Spec : ocv1.ClusterExtensionRevisionSpec {
95+ Phases : PhaseSort (objs ),
96+ Revision : 1 ,
97+ },
98+ }, nil
99+ }
100+
43101func (r * SimpleRevisionGenerator ) GenerateRevision (bundleFS fs.FS , ext * ocv1.ClusterExtension , objectLabels , revisionAnnotations map [string ]string ) (* ocv1.ClusterExtensionRevision , error ) {
44102 // extract plain manifests
45103 plain , err := r .BundleRenderer .Render (bundleFS , ext )
@@ -50,14 +108,12 @@ func (r *SimpleRevisionGenerator) GenerateRevision(bundleFS fs.FS, ext *ocv1.Clu
50108 // objectLabels
51109 objs := make ([]ocv1.ClusterExtensionRevisionObject , 0 , len (plain ))
52110 for _ , obj := range plain {
53- if len (obj .GetLabels ()) > 0 {
54- labels := maps .Clone (obj .GetLabels ())
55- if labels == nil {
56- labels = map [string ]string {}
57- }
58- maps .Copy (labels , objectLabels )
59- obj .SetLabels (labels )
111+ labels := maps .Clone (obj .GetLabels ())
112+ if labels == nil {
113+ labels = map [string ]string {}
60114 }
115+ maps .Copy (labels , objectLabels )
116+ obj .SetLabels (labels )
61117
62118 gvk , err := apiutil .GVKForObject (obj , r .Scheme )
63119 if err != nil {
@@ -94,6 +150,49 @@ func (r *SimpleRevisionGenerator) GenerateRevision(bundleFS fs.FS, ext *ocv1.Clu
94150 }, nil
95151}
96152
153+ type BoxcutterStorageMigrator struct {
154+ ActionClientGetter helmclient.ActionClientGetter
155+ RevisionGenerator ClusterExtensionRevisionGenerator
156+ Client client.Client
157+ }
158+
159+ func (m * BoxcutterStorageMigrator ) Migrate (ctx context.Context , ext * ocv1.ClusterExtension , objectLabels map [string ]string ) error {
160+ existingRevisionList := ocv1.ClusterExtensionRevisionList {}
161+ if err := m .Client .List (ctx , & existingRevisionList , client.MatchingLabels {
162+ controllers .ClusterExtensionRevisionOwnerLabel : ext .Name ,
163+ }); err != nil {
164+ return fmt .Errorf ("listing ClusterExtensionRevisions before attempting migration: %w" , err )
165+ }
166+ if len (existingRevisionList .Items ) != 0 {
167+ // No migration needed.
168+ return nil
169+ }
170+
171+ ac , err := m .ActionClientGetter .ActionClientFor (ctx , ext )
172+ if err != nil {
173+ return err
174+ }
175+
176+ helmRelease , err := ac .Get (ext .GetName ())
177+ if errors .Is (err , driver .ErrReleaseNotFound ) {
178+ // no Helm Release -> no prior installation.
179+ return nil
180+ }
181+ if err != nil {
182+ return err
183+ }
184+
185+ rev , err := m .RevisionGenerator .GenerateRevisionFromHelmRelease (helmRelease , ext , objectLabels )
186+ if err != nil {
187+ return err
188+ }
189+
190+ if err := m .Client .Create (ctx , rev ); err != nil {
191+ return err
192+ }
193+ return nil
194+ }
195+
97196type Boxcutter struct {
98197 Client client.Client
99198 Scheme * runtime.Scheme
@@ -289,3 +388,16 @@ func (r *RegistryV1BundleRenderer) Render(bundleFS fs.FS, ext *ocv1.ClusterExten
289388 }
290389 return r .BundleRenderer .Render (reg , ext .Spec .Namespace , render .WithTargetNamespaces (watchNamespace ), render .WithCertificateProvider (r .CertificateProvider ))
291390}
391+
392+ func splitManifestDocuments (file string ) []string {
393+ //nolint:prealloc
394+ var docs []string
395+ for _ , manifest := range strings .Split (file , "\n " ) {
396+ manifest = strings .TrimSpace (manifest )
397+ if len (manifest ) == 0 {
398+ continue
399+ }
400+ docs = append (docs , manifest )
401+ }
402+ return docs
403+ }
0 commit comments