@@ -18,7 +18,6 @@ package controllers
1818
1919import (
2020 "context"
21- "errors"
2221 "fmt"
2322 "sort"
2423 "strings"
@@ -29,6 +28,7 @@ import (
2928 kappctrlv1alpha1 "github.com/vmware-tanzu/carvel-kapp-controller/pkg/apis/kappctrl/v1alpha1"
3029 corev1 "k8s.io/api/core/v1"
3130 "k8s.io/apimachinery/pkg/api/equality"
31+ apierrors "k8s.io/apimachinery/pkg/api/errors"
3232 apimeta "k8s.io/apimachinery/pkg/api/meta"
3333 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3434 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -56,10 +56,11 @@ import (
5656type ExtensionReconciler struct {
5757 client.Client
5858 BundleProvider BundleProvider
59- HasKappApis bool
6059}
6160
62- var errkappAPIUnavailable = errors .New ("kapp-controller apis unavailable on cluster" )
61+ var (
62+ bundleVersionKey = "olm.operatorframework.io/bundleVersion"
63+ )
6364
6465//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=extensions,verbs=get;list;watch;create;update;patch;delete
6566//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=extensions/status,verbs=update;patch
@@ -146,17 +147,6 @@ func (r *ExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alpha1.Ext
146147 return ctrl.Result {}, nil
147148 }
148149
149- if ! r .HasKappApis {
150- ext .Status .InstalledBundle = nil
151- setInstalledStatusConditionFailed (& ext .Status .Conditions , errkappAPIUnavailable .Error (), ext .GetGeneration ())
152-
153- ext .Status .ResolvedBundle = nil
154- setResolvedStatusConditionUnknown (& ext .Status .Conditions , "kapp apis are unavailable" , ext .GetGeneration ())
155-
156- setDeprecationStatusesUnknown (& ext .Status .Conditions , "kapp apis are unavailable" , ext .GetGeneration ())
157- return ctrl.Result {}, errkappAPIUnavailable
158- }
159-
160150 // TODO: Improve the resolution logic.
161151 bundle , err := r .resolve (ctx , * ext )
162152 if err != nil {
@@ -190,7 +180,13 @@ func (r *ExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alpha1.Ext
190180 return ctrl.Result {}, nil
191181 }
192182
193- app := r .GenerateExpectedApp (* ext , bundle .Image )
183+ app , err := r .GenerateExpectedApp (* ext , bundle )
184+ if err != nil {
185+ setInstalledStatusConditionUnknown (& ext .Status .Conditions , err .Error (), ext .GetGeneration ())
186+ setDeprecationStatusesUnknown (& ext .Status .Conditions , "deprecation checks have not been attempted as installation has failed" , ext .GetGeneration ())
187+ return ctrl.Result {}, err
188+ }
189+
194190 if err := r .ensureApp (ctx , app ); err != nil {
195191 // originally Reason: ocv1alpha1.ReasonInstallationFailed
196192 ext .Status .InstalledBundle = nil
@@ -403,7 +399,13 @@ func extensionRequestsForCatalog(c client.Reader, logger logr.Logger) handler.Ma
403399 }
404400}
405401
406- func (r * ExtensionReconciler ) GenerateExpectedApp (o ocv1alpha1.Extension , bundlePath string ) * unstructured.Unstructured {
402+ func (r * ExtensionReconciler ) GenerateExpectedApp (o ocv1alpha1.Extension , bundle * catalogmetadata.Bundle ) (* unstructured.Unstructured , error ) {
403+ bundleVersion , err := bundle .Version ()
404+ if err != nil {
405+ return nil , fmt .Errorf ("failed to generate App from Extension %q with bundle %q: %w" , o .GetName (), bundle .Name , err )
406+ }
407+ bundlePath := bundle .Image
408+
407409 // We use unstructured here to avoid problems of serializing default values when sending patches to the apiserver.
408410 // If you use a typed object, any default values from that struct get serialized into the JSON patch, which could
409411 // cause unrelated fields to be patched back to the default value even though that isn't the intention. Using an
@@ -437,6 +439,9 @@ func (r *ExtensionReconciler) GenerateExpectedApp(o ocv1alpha1.Extension, bundle
437439 "metadata" : map [string ]interface {}{
438440 "name" : o .GetName (),
439441 "namespace" : o .GetNamespace (),
442+ "annotations" : map [string ]string {
443+ bundleVersionKey : bundleVersion .String (),
444+ },
440445 },
441446 "spec" : spec ,
442447 },
@@ -452,7 +457,24 @@ func (r *ExtensionReconciler) GenerateExpectedApp(o ocv1alpha1.Extension, bundle
452457 BlockOwnerDeletion : ptr .To (true ),
453458 },
454459 })
455- return app
460+ return app , nil
461+ }
462+
463+ func (r * ExtensionReconciler ) getInstalledVersion (ctx context.Context , namespacedName types.NamespacedName ) (* bsemver.Version , error ) {
464+ existingApp , err := r .existingAppUnstructured (ctx , namespacedName .Name , namespacedName .Namespace )
465+ if err != nil {
466+ return nil , err
467+ }
468+ existingVersion , ok := existingApp .GetAnnotations ()[bundleVersionKey ]
469+ if ! ok {
470+ return nil , fmt .Errorf ("existing App %q in Namespace %q missing bundle version" , namespacedName .Name , namespacedName .Namespace )
471+ }
472+
473+ existingVersionSemver , err := bsemver .New (existingVersion )
474+ if err != nil {
475+ return nil , fmt .Errorf ("could not determine bundle version of existing App %q in Namespace %q: %w" , namespacedName .Name , namespacedName .Namespace , err )
476+ }
477+ return existingVersionSemver , nil
456478}
457479
458480func (r * ExtensionReconciler ) resolve (ctx context.Context , extension ocv1alpha1.Extension ) (* catalogmetadata.Bundle , error ) {
@@ -481,19 +503,33 @@ func (r *ExtensionReconciler) resolve(ctx context.Context, extension ocv1alpha1.
481503 predicates = append (predicates , catalogfilter .InMastermindsSemverRange (vr ))
482504 }
483505
506+ var installedVersion string
507+ // Do not include bundle versions older than currently installed unless UpgradeConstraintPolicy = 'Ignore'
508+ if extension .Spec .Source .Package .UpgradeConstraintPolicy != ocv1alpha1 .UpgradeConstraintPolicyIgnore {
509+ installedVersionSemver , err := r .getInstalledVersion (ctx , types.NamespacedName {Name : extension .GetName (), Namespace : extension .GetNamespace ()})
510+ if err != nil && ! apierrors .IsNotFound (err ) {
511+ return nil , err
512+ }
513+ if installedVersionSemver != nil {
514+ installedVersion = installedVersionSemver .String ()
515+ predicates = append (predicates , catalogfilter .HigherBundleVersion (installedVersionSemver ))
516+ }
517+ }
518+
484519 resultSet := catalogfilter .Filter (allBundles , catalogfilter .And (predicates ... ))
485520
486521 if len (resultSet ) == 0 {
487- if versionRange != "" && channelName != "" {
488- return nil , fmt .Errorf ("no package %q matching version %q found in channel %q" , packageName , versionRange , channelName )
489- }
522+ var versionError , channelError , existingVersionError string
490523 if versionRange != "" {
491- return nil , fmt .Errorf ( "no package %q matching version %q found" , packageName , versionRange )
524+ versionError = fmt .Sprintf ( " matching version %q" , versionRange )
492525 }
493526 if channelName != "" {
494- return nil , fmt .Errorf ("no package %q found in channel %q" , packageName , channelName )
527+ channelError = fmt .Sprintf (" in channel %q" , channelName )
528+ }
529+ if installedVersion != "" {
530+ existingVersionError = fmt .Sprintf (" which upgrades currently installed version %q" , installedVersion )
495531 }
496- return nil , fmt .Errorf ("no package %q found" , packageName )
532+ return nil , fmt .Errorf ("no package %q%s%s%s found" , packageName , versionError , channelError , existingVersionError )
497533 }
498534
499535 sort .SliceStable (resultSet , func (i , j int ) bool {
0 commit comments