@@ -11,18 +11,24 @@ 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"
2327
28+ helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client"
2429 ocv1 "github.com/operator-framework/operator-controller/api/v1"
2530 "github.com/operator-framework/operator-controller/internal/operator-controller/controllers"
31+ "github.com/operator-framework/operator-controller/internal/operator-controller/labels"
2632 "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/bundle/source"
2733 "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render"
2834)
@@ -33,13 +39,64 @@ const (
3339
3440type ClusterExtensionRevisionGenerator interface {
3541 GenerateRevision (bundleFS fs.FS , ext * ocv1.ClusterExtension , objectLabels , revisionAnnotations map [string ]string ) (* ocv1.ClusterExtensionRevision , error )
42+ GenerateRevisionFromHelmRelease (
43+ helmRelease * release.Release , ext * ocv1.ClusterExtension ,
44+ objectLabels map [string ]string ,
45+ ) (* ocv1.ClusterExtensionRevision , error )
3646}
3747
3848type SimpleRevisionGenerator struct {
3949 Scheme * runtime.Scheme
4050 BundleRenderer BundleRenderer
4151}
4252
53+ func (r * SimpleRevisionGenerator ) GenerateRevisionFromHelmRelease (
54+ helmRelease * release.Release , ext * ocv1.ClusterExtension ,
55+ objectLabels map [string ]string ,
56+ ) (* ocv1.ClusterExtensionRevision , error ) {
57+ docs := splitManifestDocuments (helmRelease .Manifest )
58+ objs := make ([]ocv1.ClusterExtensionRevisionObject , 0 , len (docs ))
59+ for _ , doc := range docs {
60+ obj := unstructured.Unstructured {}
61+ if err := yaml .Unmarshal ([]byte (doc ), & obj ); err != nil {
62+ return nil , err
63+ }
64+
65+ labels := maps .Clone (obj .GetLabels ())
66+ if labels == nil {
67+ labels = map [string ]string {}
68+ }
69+ maps .Copy (labels , objectLabels )
70+ obj .SetLabels (labels )
71+ obj .SetOwnerReferences (nil ) // reset OwnerReferences for migration.
72+
73+ objs = append (objs , ocv1.ClusterExtensionRevisionObject {
74+ Object : obj ,
75+ CollisionProtection : ocv1 .CollisionProtectionNone , // allow to adopt objects from previous release
76+ })
77+ }
78+
79+ // Build desired revision
80+ return & ocv1.ClusterExtensionRevision {
81+ ObjectMeta : metav1.ObjectMeta {
82+ Annotations : map [string ]string {
83+ labels .BundleNameKey : helmRelease .Labels [labels .BundleNameKey ],
84+ labels .PackageNameKey : helmRelease .Labels [labels .PackageNameKey ],
85+ labels .BundleVersionKey : helmRelease .Labels [labels .BundleVersionKey ],
86+ labels .BundleReferenceKey : helmRelease .Labels [labels .BundleReferenceKey ],
87+ },
88+ Labels : map [string ]string {
89+ controllers .ClusterExtensionRevisionOwnerLabel : ext .Name ,
90+ },
91+ Name : fmt .Sprintf ("%s-1" , ext .Name ),
92+ },
93+ Spec : ocv1.ClusterExtensionRevisionSpec {
94+ Phases : PhaseSort (objs ),
95+ Revision : 1 ,
96+ },
97+ }, nil
98+ }
99+
43100func (r * SimpleRevisionGenerator ) GenerateRevision (bundleFS fs.FS , ext * ocv1.ClusterExtension , objectLabels , revisionAnnotations map [string ]string ) (* ocv1.ClusterExtensionRevision , error ) {
44101 // extract plain manifests
45102 plain , err := r .BundleRenderer .Render (bundleFS , ext )
@@ -50,14 +107,12 @@ func (r *SimpleRevisionGenerator) GenerateRevision(bundleFS fs.FS, ext *ocv1.Clu
50107 // objectLabels
51108 objs := make ([]ocv1.ClusterExtensionRevisionObject , 0 , len (plain ))
52109 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 )
110+ labels := maps .Clone (obj .GetLabels ())
111+ if labels == nil {
112+ labels = map [string ]string {}
60113 }
114+ maps .Copy (labels , objectLabels )
115+ obj .SetLabels (labels )
61116
62117 gvk , err := apiutil .GVKForObject (obj , r .Scheme )
63118 if err != nil {
@@ -94,6 +149,49 @@ func (r *SimpleRevisionGenerator) GenerateRevision(bundleFS fs.FS, ext *ocv1.Clu
94149 }, nil
95150}
96151
152+ type BoxcutterStorageMigrator struct {
153+ ActionClientGetter helmclient.ActionClientGetter
154+ RevisionGenerator ClusterExtensionRevisionGenerator
155+ Client client.Client
156+ }
157+
158+ func (m * BoxcutterStorageMigrator ) Migrate (ctx context.Context , ext * ocv1.ClusterExtension , objectLabels map [string ]string ) error {
159+ existingRevisionList := ocv1.ClusterExtensionRevisionList {}
160+ if err := m .Client .List (ctx , & existingRevisionList , client.MatchingLabels {
161+ controllers .ClusterExtensionRevisionOwnerLabel : ext .Name ,
162+ }); err != nil {
163+ return fmt .Errorf ("listing ClusterExtensionRevisions before attempting migration: %w" , err )
164+ }
165+ if len (existingRevisionList .Items ) != 0 {
166+ // No migration needed.
167+ return nil
168+ }
169+
170+ ac , err := m .ActionClientGetter .ActionClientFor (ctx , ext )
171+ if err != nil {
172+ return err
173+ }
174+
175+ helmRelease , err := ac .Get (ext .GetName ())
176+ if errors .Is (err , driver .ErrReleaseNotFound ) {
177+ // no Helm Release -> no prior installation.
178+ return nil
179+ }
180+ if err != nil {
181+ return err
182+ }
183+
184+ rev , err := m .RevisionGenerator .GenerateRevisionFromHelmRelease (helmRelease , ext , objectLabels )
185+ if err != nil {
186+ return err
187+ }
188+
189+ if err := m .Client .Create (ctx , rev ); err != nil {
190+ return err
191+ }
192+ return nil
193+ }
194+
97195type Boxcutter struct {
98196 Client client.Client
99197 Scheme * runtime.Scheme
@@ -289,3 +387,14 @@ func (r *RegistryV1BundleRenderer) Render(bundleFS fs.FS, ext *ocv1.ClusterExten
289387 }
290388 return r .BundleRenderer .Render (reg , ext .Spec .Namespace , render .WithTargetNamespaces (watchNamespace ), render .WithCertificateProvider (r .CertificateProvider ))
291389}
390+
391+ func splitManifestDocuments (file string ) (docs []string ) {
392+ for _ , yamlDocument := range strings .Split (file , "\n " ) {
393+ yamlDocument = strings .TrimSpace (string (yamlDocument ))
394+ if len (yamlDocument ) == 0 {
395+ continue
396+ }
397+ docs = append (docs , yamlDocument )
398+ }
399+ return docs
400+ }
0 commit comments