@@ -46,6 +46,7 @@ import (
46
46
"k8s.io/apimachinery/pkg/runtime/schema"
47
47
"k8s.io/apimachinery/pkg/types"
48
48
utilerrors "k8s.io/apimachinery/pkg/util/errors"
49
+ "k8s.io/apimachinery/pkg/util/sets"
49
50
apimachyaml "k8s.io/apimachinery/pkg/util/yaml"
50
51
ctrl "sigs.k8s.io/controller-runtime"
51
52
"sigs.k8s.io/controller-runtime/pkg/cache"
@@ -63,6 +64,7 @@ import (
63
64
catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1"
64
65
helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client"
65
66
"github.com/operator-framework/operator-registry/alpha/declcfg"
67
+ "github.com/operator-framework/operator-registry/alpha/property"
66
68
rukpakv1alpha2 "github.com/operator-framework/rukpak/api/v1alpha2"
67
69
helmpredicate "github.com/operator-framework/rukpak/pkg/helm-operator-plugins/predicate"
68
70
rukpaksource "github.com/operator-framework/rukpak/pkg/source"
@@ -106,6 +108,8 @@ type ClusterExtensionReconciler struct {
106
108
//+kubebuilder:rbac:groups=catalogd.operatorframework.io,resources=catalogs,verbs=list;watch
107
109
//+kubebuilder:rbac:groups=catalogd.operatorframework.io,resources=catalogmetadata,verbs=list;watch
108
110
111
+ // The operator controller needs to watch all the bundle objects and reconcile accordingly. Though not ideal, but these permissions are required.
112
+ // This has been taken from rukpak, and an issue was created before to discuss it: https://github.com/operator-framework/rukpak/issues/800.
109
113
func (r * ClusterExtensionReconciler ) Reconcile (ctx context.Context , req ctrl.Request ) (ctrl.Result , error ) {
110
114
l := log .FromContext (ctx ).WithName ("operator-controller" )
111
115
l .V (1 ).Info ("starting" )
@@ -210,6 +214,15 @@ func (r *ClusterExtensionReconciler) handleResolutionErrors(ext *ocv1alpha1.Clus
210
214
// But in the future we might update this function
211
215
// to return different results (e.g. requeue).
212
216
//
217
+ /* The reconcile functions performs the following major tasks:
218
+ 1. Resolution: Run the resolution to find the bundle from the catalog which needs to be installed.
219
+ 2. Validate: Ensure that the bundle returned from the resolution for install meets our requirements.
220
+ 3. Unpack: Unpack the contents from the bundle and store in a localdir in the pod.
221
+ 4. Install: The process of installing involves:
222
+ 4.1 Converting the CSV in the bundle into a set of plain k8s objects.
223
+ 4.2 Generating a chart from k8s objects.
224
+ 4.3 Apply the release on cluster.
225
+ */
213
226
//nolint:unparam
214
227
func (r * ClusterExtensionReconciler ) reconcile (ctx context.Context , ext * ocv1alpha1.ClusterExtension ) (ctrl.Result , error ) {
215
228
l := log .FromContext (ctx ).WithName ("operator-controller" )
@@ -220,6 +233,12 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
220
233
return r .handleResolutionErrors (ext , err )
221
234
}
222
235
236
+ if err := r .validateBundle (bundle ); err != nil {
237
+ setInstalledStatusConditionFailed (& ext .Status .Conditions , err .Error (), ext .GetGeneration ())
238
+ setDeprecationStatusesUnknown (& ext .Status .Conditions , "deprecation checks have not been attempted as installation has failed" , ext .GetGeneration ())
239
+ return ctrl.Result {}, err
240
+ }
241
+
223
242
bundleVersion , err := bundle .Version ()
224
243
if err != nil {
225
244
ext .Status .ResolvedBundle = nil
@@ -229,14 +248,12 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
229
248
return ctrl.Result {}, err
230
249
}
231
250
232
- // Now we can set the Resolved Condition, and the resolvedBundleSource field to the bundle.Image value.
233
251
ext .Status .ResolvedBundle = bundleMetadataFor (bundle )
234
252
setResolvedStatusConditionSuccess (& ext .Status .Conditions , fmt .Sprintf ("resolved to %q" , bundle .Image ), ext .GetGeneration ())
235
253
236
- // Unpack contents into a fs based on the bundle.
237
- // Considering only image source.
238
-
239
- // Generate a BundleDeployment from the ClusterExtension to Unpack
254
+ // Generate a BundleDeployment from the ClusterExtension to Unpack.
255
+ // Note: The BundleDeployment here is not a k8s API, its a simple Go struct which
256
+ // necessary embedded values.
240
257
bd := r .generateBundleDeploymentForUnpack (bundle .Image , ext )
241
258
unpackResult , err := r .Unpacker .Unpack (ctx , bd )
242
259
if err != nil {
@@ -253,6 +270,8 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
253
270
updateStatusUnpacking (& ext .Status , unpackResult )
254
271
return ctrl.Result {}, nil
255
272
case rukpaksource .StateUnpacked :
273
+ // TODO: Add finalizer to clean the stored bundles, after https://github.com/operator-framework/rukpak/pull/897
274
+ // merges.
256
275
if err := r .Storage .Store (ctx , ext , unpackResult .Bundle ); err != nil {
257
276
return ctrl.Result {}, updateStatusUnpackFailing (& ext .Status , err )
258
277
}
@@ -302,7 +321,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
302
321
303
322
rel , state , err := r .getReleaseState (ac , ext , chrt , values , post )
304
323
if err != nil {
305
- setInstalledStatusConditionFailed (& ext .Status .Conditions , fmt .Sprintf ("%s:%v" , ocv1alpha1 .ReasonErrorGettingReleaseState , err ), ext .Generation )
324
+ setInstalledStatusConditionFailed (& ext .Status .Conditions , fmt .Sprintf ("%s:%v" , rukpakv1alpha2 .ReasonErrorGettingReleaseState , err ), ext .Generation )
306
325
return ctrl.Result {}, err
307
326
}
308
327
@@ -343,14 +362,14 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
343
362
344
363
relObjects , err := util .ManifestObjects (strings .NewReader (rel .Manifest ), fmt .Sprintf ("%s-release-manifest" , rel .Name ))
345
364
if err != nil {
346
- setInstalledStatusConditionFailed (& ext .Status .Conditions , fmt .Sprintf ("%s:%v" , ocv1alpha1 .ReasonCreateDynamicWatchFailed , err ), ext .Generation )
365
+ setInstalledStatusConditionFailed (& ext .Status .Conditions , fmt .Sprintf ("%s:%v" , rukpakv1alpha2 .ReasonCreateDynamicWatchFailed , err ), ext .Generation )
347
366
return ctrl.Result {}, err
348
367
}
349
368
350
369
for _ , obj := range relObjects {
351
370
uMap , err := runtime .DefaultUnstructuredConverter .ToUnstructured (obj )
352
371
if err != nil {
353
- setInstalledStatusConditionFailed (& ext .Status .Conditions , fmt .Sprintf ("%s:%v" , ocv1alpha1 .ReasonCreateDynamicWatchFailed , err ), ext .Generation )
372
+ setInstalledStatusConditionFailed (& ext .Status .Conditions , fmt .Sprintf ("%s:%v" , rukpakv1alpha2 .ReasonCreateDynamicWatchFailed , err ), ext .Generation )
354
373
return ctrl.Result {}, err
355
374
}
356
375
@@ -372,17 +391,17 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
372
391
return nil
373
392
}(); err != nil {
374
393
ext .Status .InstalledBundle = nil
375
- setInstalledStatusConditionFailed (& ext .Status .Conditions , fmt .Sprintf ("%s:%v" , ocv1alpha1 .ReasonCreateDynamicWatchFailed , err ), ext .Generation )
394
+ setInstalledStatusConditionFailed (& ext .Status .Conditions , fmt .Sprintf ("%s:%v" , rukpakv1alpha2 .ReasonCreateDynamicWatchFailed , err ), ext .Generation )
376
395
return ctrl.Result {}, err
377
396
}
378
397
}
379
398
ext .Status .InstalledBundle = bundleMetadataFor (bundle )
380
399
setInstalledStatusConditionSuccess (& ext .Status .Conditions , fmt .Sprintf ("Instantiated bundle %s successfully" , ext .GetName ()), ext .Generation )
381
400
382
- // set the status of the cluster extension based on the respective bundle deployment status conditions.
383
401
return ctrl.Result {}, nil
384
402
}
385
403
404
+ // resolve returns a Bundle from the catalog that needs to get installed on the cluster.
386
405
func (r * ClusterExtensionReconciler ) resolve (ctx context.Context , ext ocv1alpha1.ClusterExtension ) (* catalogmetadata.Bundle , error ) {
387
406
allBundles , err := r .BundleProvider .Bundles (ctx )
388
407
if err != nil {
@@ -663,17 +682,14 @@ func clusterExtensionRequestsForCatalog(c client.Reader, logger logr.Logger) crh
663
682
}
664
683
}
665
684
685
+ // getInstalledVersion fetches the installed version of a particular bundle from the cluster. To do so, we read the release label
686
+ // which are added during install.
666
687
func (r * ClusterExtensionReconciler ) getInstalledVersion (ctx context.Context , clusterExtension ocv1alpha1.ClusterExtension ) (* bsemver.Version , error ) {
667
688
cl , err := r .ActionClientGetter .ActionClientFor (ctx , & clusterExtension )
668
689
if err != nil {
669
690
return nil , err
670
691
}
671
692
672
- // Clarify - Every release will have a unique name as the cluster extension?
673
- // Also filter relases whose owner is the operator controller?
674
- // I think this should work, given we are setting the release Name to the clusterExtension name.
675
- // If not, the other option is to get the Helm secret in the release namespace, list all the releases,
676
- // get the chart annotations.
677
693
release , err := cl .Get (clusterExtension .GetName ())
678
694
if err != nil && ! errors .Is (err , driver .ErrReleaseNotFound ) {
679
695
return nil , err
@@ -682,7 +698,6 @@ func (r *ClusterExtensionReconciler) getInstalledVersion(ctx context.Context, cl
682
698
return nil , nil
683
699
}
684
700
685
- // TODO: when the chart is created these annotations are to be added.
686
701
existingVersion , ok := release .Labels [labels .BundleVersionKey ]
687
702
if ! ok {
688
703
return nil , fmt .Errorf ("release %q: missing bundle version" , release .Name )
@@ -804,3 +819,22 @@ func bundleMetadataFor(bundle *catalogmetadata.Bundle) *ocv1alpha1.BundleMetadat
804
819
Version : ver .String (),
805
820
}
806
821
}
822
+
823
+ func (r * ClusterExtensionReconciler ) validateBundle (bundle * catalogmetadata.Bundle ) error {
824
+ unsupportedProps := sets .New (
825
+ property .TypePackageRequired ,
826
+ property .TypeGVKRequired ,
827
+ property .TypeConstraint ,
828
+ )
829
+ for i := range bundle .Properties {
830
+ if unsupportedProps .Has (bundle .Properties [i ].Type ) {
831
+ return fmt .Errorf (
832
+ "bundle %q has a dependency declared via property %q which is currently not supported" ,
833
+ bundle .Name ,
834
+ bundle .Properties [i ].Type ,
835
+ )
836
+ }
837
+ }
838
+
839
+ return nil
840
+ }
0 commit comments