@@ -7,11 +7,14 @@ import (
7
7
"fmt"
8
8
"io"
9
9
"io/fs"
10
+ "path/filepath"
11
+ "regexp"
10
12
"slices"
11
13
"strings"
12
14
13
15
"helm.sh/helm/v3/pkg/action"
14
16
"helm.sh/helm/v3/pkg/chart"
17
+ "helm.sh/helm/v3/pkg/chart/loader"
15
18
"helm.sh/helm/v3/pkg/chartutil"
16
19
"helm.sh/helm/v3/pkg/postrender"
17
20
"helm.sh/helm/v3/pkg/release"
@@ -26,6 +29,7 @@ import (
26
29
27
30
ocv1 "github.com/operator-framework/operator-controller/api/v1"
28
31
"github.com/operator-framework/operator-controller/internal/operator-controller/authorization"
32
+ "github.com/operator-framework/operator-controller/internal/operator-controller/features"
29
33
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/bundle/source"
30
34
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/preflights/crdupgradesafety"
31
35
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util"
@@ -209,7 +213,119 @@ func (h *Helm) buildHelmChart(bundleFS fs.FS, ext *ocv1.ClusterExtension) (*char
209
213
if err != nil {
210
214
return nil , err
211
215
}
212
- return h .BundleToHelmChartConverter .ToHelmChart (source .FromFS (bundleFS ), ext .Spec .Namespace , watchNamespace )
216
+ var result * chart.Chart
217
+ contentType := bundleContentType (bundleFS )
218
+ switch contentType {
219
+ case HelmContent :
220
+ if ! features .OperatorControllerFeatureGate .Enabled (features .HelmChartSupport ) {
221
+ return nil , fmt .Errorf ("helm chart support is not enabled" )
222
+ }
223
+ result , err = loadChartWithEnrichments (bundleFS , WithInstallNamespace (ext .Spec .Namespace ))
224
+ if err != nil {
225
+ return nil , fmt .Errorf ("loading Helm chart; %w" , err )
226
+ }
227
+ case BundleContent :
228
+ result , err = h .BundleToHelmChartConverter .ToHelmChart (source .FromFS (bundleFS ), ext .Spec .Namespace , watchNamespace )
229
+ if err != nil {
230
+ return nil , err
231
+ }
232
+ default :
233
+ return nil , fmt .Errorf ("unknown content found" )
234
+ }
235
+
236
+ return result , nil
237
+ }
238
+
239
+ const (
240
+ HelmContent string = "helm"
241
+ BundleContent string = "bundle"
242
+ UnknownContent string = "unknown"
243
+ )
244
+
245
+ func bundleContentType (bundleFS fs.FS ) string {
246
+ var contentType string
247
+ _ = fs .WalkDir (bundleFS , "." , func (path string , f fs.DirEntry , err error ) error {
248
+ if err != nil {
249
+ return err
250
+ }
251
+
252
+ if ! f .IsDir () {
253
+ // Check if Helm chart
254
+ if filepath .Dir (path ) == "charts" &&
255
+ filepath .Ext (f .Name ()) == ".tgz" {
256
+ contentType = HelmContent
257
+ return fs .SkipAll
258
+ }
259
+
260
+ // Check if registry/v1 bundle
261
+ if filepath .Dir (path ) == "metadata" &&
262
+ f .Name () == "annotations.yaml" {
263
+ contentType = BundleContent
264
+ return fs .SkipAll
265
+ }
266
+ }
267
+
268
+ return nil
269
+ })
270
+
271
+ if contentType != "" {
272
+ return contentType
273
+ }
274
+
275
+ return UnknownContent
276
+ }
277
+
278
+ type ChartOption func (* chart.Chart )
279
+
280
+ func WithInstallNamespace (namespace string ) ChartOption {
281
+ re := regexp .MustCompile (`{{\W+\.Release\.Namespace\W+}}` )
282
+
283
+ return func (chrt * chart.Chart ) {
284
+ for i , template := range chrt .Templates {
285
+ chrt .Templates [i ].Data = re .ReplaceAll (template .Data , []byte (namespace ))
286
+ }
287
+ }
288
+ }
289
+
290
+ func loadChartWithEnrichments (bundleFS fs.FS , options ... ChartOption ) (* chart.Chart , error ) {
291
+ chrt , err := loadPulledHelmChart (bundleFS )
292
+ if err != nil {
293
+ return nil , err
294
+ }
295
+
296
+ for _ , f := range options {
297
+ f (chrt )
298
+ }
299
+
300
+ return chrt , nil
301
+ }
302
+
303
+ func loadPulledHelmChart (bundleFS fs.FS ) (* chart.Chart , error ) {
304
+ var filename string
305
+
306
+ if err := fs .WalkDir (bundleFS , "." , func (path string , f fs.DirEntry , err error ) error {
307
+ if err != nil {
308
+ return err
309
+ }
310
+ if strings .HasSuffix (f .Name (), ".tgz" ) && ! f .IsDir () {
311
+ filename = path
312
+ return fs .SkipAll
313
+ }
314
+
315
+ return nil
316
+ }); err != nil && ! errors .Is (err , fs .SkipAll ) {
317
+ return nil , err
318
+ }
319
+
320
+ if filename == "" {
321
+ return nil , fmt .Errorf ("no helm chart found" )
322
+ }
323
+
324
+ tarball , err := fs .ReadFile (bundleFS , filename )
325
+ if err != nil {
326
+ return nil , fmt .Errorf ("reading helm chart; %+v" , err )
327
+ }
328
+ return loader .LoadArchive (bytes .NewBuffer (tarball ))
213
329
}
214
330
215
331
func (h * Helm ) renderClientOnlyRelease (ctx context.Context , ext * ocv1.ClusterExtension , chrt * chart.Chart , values chartutil.Values , post postrender.PostRenderer ) (* release.Release , error ) {
0 commit comments