diff --git a/api/v1alpha1/clusterextension_types.go b/api/v1alpha1/clusterextension_types.go index a72906aa6..bb49e5f0d 100644 --- a/api/v1alpha1/clusterextension_types.go +++ b/api/v1alpha1/clusterextension_types.go @@ -173,7 +173,3 @@ func (r *ClusterExtension) GetPackageSpec() *ExtensionSourcePackage { func (r *ClusterExtension) GetUID() types.UID { return r.ObjectMeta.GetUID() } - -func (r *ClusterExtension) GetUpgradeConstraintPolicy() UpgradeConstraintPolicy { - return r.Spec.UpgradeConstraintPolicy -} diff --git a/api/v1alpha1/extension_types.go b/api/v1alpha1/extension_types.go index 74b102f54..9be080a39 100644 --- a/api/v1alpha1/extension_types.go +++ b/api/v1alpha1/extension_types.go @@ -132,10 +132,3 @@ func (r *Extension) GetPackageSpec() *ExtensionSourcePackage { func (r *Extension) GetUID() types.UID { return r.ObjectMeta.GetUID() } - -func (r *Extension) GetUpgradeConstraintPolicy() UpgradeConstraintPolicy { - if r.Spec.Source.Package != nil { - return r.Spec.Source.Package.UpgradeConstraintPolicy - } - return "" -} diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 046e4256b..c929b53ee 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -123,16 +123,15 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "ClusterExtension") os.Exit(1) } - if features.OperatorControllerFeatureGate.Enabled(features.EnableExtensionAPI) { - if err = (&controllers.ExtensionReconciler{ - Client: cl, - BundleProvider: catalogClient, - Scheme: mgr.GetScheme(), - Resolver: resolver, - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "Extension") - os.Exit(1) - } + + if err = (&controllers.ExtensionReconciler{ + Client: cl, + BundleProvider: catalogClient, + Scheme: mgr.GetScheme(), + Resolver: resolver, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Extension") + os.Exit(1) } //+kubebuilder:scaffold:builder diff --git a/config/rbac/extension_editor_role.yaml b/config/rbac/extension_editor_role.yaml index 1bf3e16d2..caa26cfd2 100644 --- a/config/rbac/extension_editor_role.yaml +++ b/config/rbac/extension_editor_role.yaml @@ -2,13 +2,6 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - labels: - app.kubernetes.io/name: clusterrole - app.kubernetes.io/instance: extension-editor-role - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: operator-controller - app.kubernetes.io/part-of: operator-controller - app.kubernetes.io/managed-by: kustomize name: extension-editor-role rules: - apiGroups: @@ -23,9 +16,3 @@ rules: - patch - update - watch -- apiGroups: - - olm.operatorframework.io - resources: - - extensions/status - verbs: - - get diff --git a/config/rbac/extension_viewer_role.yaml b/config/rbac/extension_viewer_role.yaml index dbe45d38b..980be2d77 100644 --- a/config/rbac/extension_viewer_role.yaml +++ b/config/rbac/extension_viewer_role.yaml @@ -2,13 +2,6 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - labels: - app.kubernetes.io/name: clusterrole - app.kubernetes.io/instance: extension-viewer-role - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: operator-controller - app.kubernetes.io/part-of: operator-controller - app.kubernetes.io/managed-by: kustomize name: extension-viewer-role rules: - apiGroups: @@ -19,9 +12,3 @@ rules: - get - list - watch -- apiGroups: - - olm.operatorframework.io - resources: - - extensions/status - verbs: - - get diff --git a/config/samples/olm_v1alpha1_extension.yaml b/config/samples/olm_v1alpha1_extension.yaml index 8f3555391..72450583c 100644 --- a/config/samples/olm_v1alpha1_extension.yaml +++ b/config/samples/olm_v1alpha1_extension.yaml @@ -1,12 +1,6 @@ apiVersion: olm.operatorframework.io/v1alpha1 kind: Extension metadata: - labels: - app.kubernetes.io/name: extension - app.kubernetes.io/instance: extension-sample - app.kubernetes.io/part-of: operator-controller - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/created-by: operator-controller name: extension-sample namespace: extension-namespace spec: diff --git a/internal/controllers/extension_controller.go b/internal/controllers/extension_controller.go index 37d9018c3..03a7b341f 100644 --- a/internal/controllers/extension_controller.go +++ b/internal/controllers/extension_controller.go @@ -21,20 +21,18 @@ import ( "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" utilerrors "k8s.io/apimachinery/pkg/util/errors" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "github.com/go-logr/logr" catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" "github.com/operator-framework/deppy/pkg/deppy/solver" ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" "github.com/operator-framework/operator-controller/internal/controllers/validators" + "github.com/operator-framework/operator-controller/pkg/features" ) // ExtensionReconciler reconciles a Extension object @@ -102,9 +100,29 @@ func (*ExtensionReconciler) checkForUnexpectedFieldChange(a, b ocv1alpha1.Extens // to return different results (e.g. requeue). // //nolint:unparam -func (r *ExtensionReconciler) reconcile(_ context.Context, ext *ocv1alpha1.Extension) (ctrl.Result, error) { +func (r *ExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alpha1.Extension) (ctrl.Result, error) { + l := log.FromContext(ctx).WithName("extension-controller") + + // Don't do anything if feature gated + if !features.OperatorControllerFeatureGate.Enabled(features.EnableExtensionAPI) { + l.Info("extension feature is gated", "name", ext.GetName(), "namespace", ext.GetNamespace()) + + // Set the TypeInstalled condition to Unknown to indicate that the resolution + // hasn't been attempted yet, due to the spec being invalid. + ext.Status.InstalledBundleResource = "" + setInstalledStatusConditionUnknown(&ext.Status.Conditions, "extension feature is disabled", ext.GetGeneration()) + // Set the TypeResolved condition to Unknown to indicate that the resolution + // hasn't been attempted yet, due to the spec being invalid. + ext.Status.ResolvedBundleResource = "" + setResolvedStatusConditionUnknown(&ext.Status.Conditions, "extension feature is disabled", ext.GetGeneration()) + + setDeprecationStatusesUnknown(&ext.Status.Conditions, "extension feature is disabled", ext.GetGeneration()) + return ctrl.Result{}, nil + } + // Don't do anything if Paused if ext.Spec.Managed == ocv1alpha1.ManagedStatePaused { + l.Info("resource is paused", "name", ext.GetName(), "namespace", ext.GetNamespace()) return ctrl.Result{}, nil } @@ -146,32 +164,16 @@ func (r *ExtensionReconciler) reconcile(_ context.Context, ext *ocv1alpha1.Exten // SetupWithManager sets up the controller with the Manager. func (r *ExtensionReconciler) SetupWithManager(mgr ctrl.Manager) error { // TODO: Add watch for kapp-controller resources + + // When feature-gated, don't watch catalogs. + if !features.OperatorControllerFeatureGate.Enabled(features.EnableExtensionAPI) { + return ctrl.NewControllerManagedBy(mgr). + For(&ocv1alpha1.Extension{}). + Complete(r) + } + return ctrl.NewControllerManagedBy(mgr). For(&ocv1alpha1.Extension{}). - Watches(&catalogd.Catalog{}, - handler.EnqueueRequestsFromMapFunc(extensionRequestsForCatalog(mgr.GetClient(), mgr.GetLogger()))). + Watches(&catalogd.Catalog{}, &handler.EnqueueRequestForObject{}). Complete(r) } - -// Generate reconcile requests for all extensions affected by a catalog change -func extensionRequestsForCatalog(c client.Reader, logger logr.Logger) handler.MapFunc { - return func(ctx context.Context, _ client.Object) []reconcile.Request { - // no way of associating an extension to a catalog so create reconcile requests for everything - extensions := ocv1alpha1.ExtensionList{} - err := c.List(ctx, &extensions) - if err != nil { - logger.Error(err, "unable to enqueue extensions for catalog reconcile") - return nil - } - var requests []reconcile.Request - for _, ext := range extensions.Items { - requests = append(requests, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Namespace: ext.GetNamespace(), - Name: ext.GetName(), - }, - }) - } - return requests - } -} diff --git a/internal/controllers/extension_controller_test.go b/internal/controllers/extension_controller_test.go index 213dcdaef..786a24509 100644 --- a/internal/controllers/extension_controller_test.go +++ b/internal/controllers/extension_controller_test.go @@ -12,12 +12,14 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/rand" + featuregatetesting "k8s.io/component-base/featuregate/testing" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" "github.com/operator-framework/operator-controller/internal/conditionsets" + "github.com/operator-framework/operator-controller/pkg/features" ) // Describe: Extension Controller Test @@ -108,12 +110,34 @@ func TestExtensionBadResources(t *testing.T) { require.Equal(t, ctrl.Result{}, res) require.NoError(t, err) + require.NoError(t, cl.Get(ctx, name, ext), fmt.Sprintf("Get failed on %q", e.ObjectMeta.GetName())) + cond := apimeta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeResolved) + require.NotNil(t, cond, fmt.Sprintf("Get condition failed on %q", ext.ObjectMeta.GetName())) + require.Equal(t, metav1.ConditionUnknown, cond.Status, fmt.Sprintf("Get status check failed on %q", ext.ObjectMeta.GetName())) + require.Equal(t, ocv1alpha1.ReasonResolutionUnknown, cond.Reason, fmt.Sprintf("Get status reason failed on %q", ext.ObjectMeta.GetName())) + require.Equal(t, "extension feature is disabled", cond.Message) + require.NoError(t, client.IgnoreNotFound(cl.Delete(ctx, ext))) + } + + for _, e := range invalidExtensions { + resetFeatureGate := featuregatetesting.SetFeatureGateDuringTest(t, features.OperatorControllerFeatureGate, features.EnableExtensionAPI, true) + ext := e.DeepCopy() + require.NoError(t, cl.Create(ctx, ext), fmt.Sprintf("Create failed on %q", e.GetObjectMeta().GetName())) + name := types.NamespacedName{Name: e.GetObjectMeta().GetName(), Namespace: e.GetObjectMeta().GetNamespace()} + + cl, reconciler := newClientAndExtensionReconciler(t) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: name}) + require.Equal(t, ctrl.Result{}, res) + require.NoError(t, err) + require.NoError(t, cl.Get(ctx, name, ext), fmt.Sprintf("Get failed on %q", e.ObjectMeta.GetName())) cond := apimeta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeResolved) require.NotNil(t, cond, fmt.Sprintf("Get condition failed on %q", ext.ObjectMeta.GetName())) require.Equal(t, metav1.ConditionUnknown, cond.Status, fmt.Sprintf("Get status check failed on %q", ext.ObjectMeta.GetName())) require.Equal(t, ocv1alpha1.ReasonResolutionUnknown, cond.Reason, fmt.Sprintf("Get status reason failed on %q", ext.ObjectMeta.GetName())) require.Equal(t, "validation has not been attempted as spec is invalid", cond.Message) + + resetFeatureGate() } require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.Extension{}, client.InNamespace(extKey.Namespace))) @@ -121,6 +145,7 @@ func TestExtensionBadResources(t *testing.T) { } func TestExtensionInvalidSemverPastRegex(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, features.OperatorControllerFeatureGate, features.EnableExtensionAPI, true)() cl, reconciler := newClientAndExtensionReconciler(t) ctx := context.Background() t.Log("When an invalid semver is provided that bypasses the regex validation") @@ -135,7 +160,7 @@ func TestExtensionInvalidSemverPastRegex(t *testing.T) { } require.NoError(t, cl.Create(ctx, namespace)) - t.Log("By injecting creating a client with the bad clusterextension CR") + t.Log("By injecting creating a client with the bad extension CR") pkgName := fmt.Sprintf("exists-%s", rand.String(6)) extension := &ocv1alpha1.Extension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name, Namespace: extKey.Namespace}, diff --git a/internal/internal.go b/internal/internal.go index c849962ea..d8475ec30 100644 --- a/internal/internal.go +++ b/internal/internal.go @@ -24,7 +24,6 @@ import ( type ExtensionInterface interface { GetPackageSpec() *ocv1alpha1.ExtensionSourcePackage - GetUpgradeConstraintPolicy() ocv1alpha1.UpgradeConstraintPolicy GetUID() types.UID }