Repository: cloudnative-pg/cloudnative-pg

Issue:
[Feature]: Deprecation of imperative hibernation via the plugin
### Is there an existing issue already for this feature request/idea?

- [x] I have searched for an existing issue, and could not find anything. I believe this is a new feature request to be evaluated.

### What problem is this feature going to solve? Why should it be added?

The [imperative hibernation](https://cloudnative-pg.io/documentation/current/kubectl-plugin/#cluster-hibernation) capability was created to explore the feature a couple of years ago and was delegated to the `cnpg hibernate on/off` command.

We learned from that exercise to develop the [declarative hibernation process](https://cloudnative-pg.io/documentation/current/declarative_hibernation/).

The two implementations differ consistently, as the declarative one simply shuts down the pods, preserving all the PVCs, while the imperative one only keeps the primary PVCs.

This is creating usability issues and confusion among the users.

### Describe the solution you'd like

We should converge towards the declarative one. As a result, the `cnpg hibernate on/off` command will be simply a proxy for setting or removing the `cnpg.io/hibernation` annotation.

Starting from 1.26.0.


### Describe alternatives you've considered

N/A

### Additional context

See #6653 #6550 

Make sure this page is also updated: [declarative_hibernation](https://cloudnative-pg.io/documentation/current/declarative_hibernation/)

### Backport?

No

### Are you willing to actively contribute to this feature?

Yes

### Code of Conduct

- [x] I agree to follow this project's Code of Conduct

Discussion:
@jobo-prod: @gbartolini I appreciate this feature! Just out of curiosity – is there a specific reason for controlling that by an annotation instead of a `.spec.hibernate: true` field or similar?

It took me a bit to find out about this because this way, it's not part of the resource schema. So, e.g., you won't find it with
```
kubectl explain cluster.spec
```

Solution patch:
diff --git a/docs/src/declarative_hibernation.md b/docs/src/declarative_hibernation.md
index 36eda0bf..a5cbeae3 100644
--- a/docs/src/declarative_hibernation.md
+++ b/docs/src/declarative_hibernation.md
@@ -12,14 +12,6 @@ process is running.
 The declarative hibernation feature enables saving CPU power by removing the
 database Pods, while keeping the database PVCs.
 
-!!! Note
-    Declarative hibernation is different from the existing implementation
-    of [imperative hibernation via the `cnpg` plugin](kubectl-plugin.md#cluster-hibernation).
-    Imperative hibernation shuts down all Postgres instances in the High
-    Availability cluster, and keeps a static copy of the PVCs of the primary that
-    contain `PGDATA` and WALs. The plugin enables to exit the hibernation phase, by
-    resuming the primary and then recreating all the replicas - if they exist.
-
 ## Hibernation
 
 To hibernate a cluster, set the `cnpg.io/hibernation=on` annotation:
diff --git a/docs/src/kubectl-plugin.md b/docs/src/kubectl-plugin.md
index 4048b9b7..7663c1b3 100644
--- a/docs/src/kubectl-plugin.md
+++ b/docs/src/kubectl-plugin.md
@@ -895,62 +895,48 @@ PVCs:
 kubectl cnpg destroy cluster-example 2
 ```
 
-### Cluster hibernation
+### Cluster Hibernation
 
-Sometimes you may want to suspend the execution of a CloudNativePG `Cluster`
-while retaining its data, then resume its activity at a later time. We've
-called this feature **cluster hibernation**.
+There are times when you may need to temporarily suspend a CloudNativePG
+`Cluster` while preserving its data, allowing you to resume operations later.
+This feature is known as **cluster hibernation**.
 
-Hibernation is only available via the `kubectl cnpg hibernate [on|off]`
-commands.
+Hibernation is managed declaratively using the `cnpg.io/hibernation`
+annotation.
 
-Hibernating a CloudNativePG cluster means destroying all the resources
-generated by the cluster, except the PVCs that belong to the PostgreSQL primary
-instance.
+!!! Info
+    For more details, see the ["Declarative Hibernation"](declarative_hibernation.md)
+    documentation page.
+
+To simplify the process, the `cnpg` plugin for `kubectl` provides a `hibernate`
+command, which acts as a convenient shortcut for applying the annotation.
 
-You can hibernate a cluster with:
+To hibernate a cluster, run:
 
 ```sh
 kubectl cnpg hibernate on CLUSTER
 ```
 
-This will:
-
-1. shutdown every PostgreSQL instance
-2. detach the PVCs containing the data of the primary instance, and annotate
-   them with the latest database status and the latest cluster configuration
-3. delete the `Cluster` resource, including every generated resource - except
-   the aforementioned PVCs
-
-When hibernated, a CloudNativePG cluster is represented by just a group of
-PVCs, in which the one containing the `PGDATA` is annotated with the latest
-available status, including content from `pg_controldata`.
-
-!!! Warning
-    A cluster having fenced instances cannot be hibernated, as fencing is
-    part of the hibernation procedure too.
-
-In case of error the operator will not be able to revert the procedure. You can
-still force the operation with:
-
-```sh
-kubectl cnpg hibernate on CLUSTER --force
-```
+This command applies the `cnpg.io/hibernation=on` annotation to the cluster,
+suspending its execution.
 
-A hibernated cluster can be resumed with:
+To resume a hibernated cluster, use:
 
 ```sh
 kubectl cnpg hibernate off CLUSTER
 ```
 
-Once the cluster has been hibernated, it's possible to show the last
-configuration and the status that PostgreSQL had after it was shut down.
-That can be done with:
+This will remove the hibernation state by setting `cnpg.io/hibernation=off`.
+
+You can check the cluster’s status at any time with:
 
 ```sh
-kubectl cnpg hibernate status CLUSTER
+kubectl cnpg status CLUSTER
 ```
 
+This will display the current state of the cluster, including whether it is
+hibernated.
+
 ### Benchmarking the database with pgbench
 
 Pgbench can be run against an existing PostgreSQL cluster with following
diff --git a/docs/src/release_notes/v1.26.md b/docs/src/release_notes/v1.26.md
index ca52d5e2..17f9f6b6 100644
--- a/docs/src/release_notes/v1.26.md
+++ b/docs/src/release_notes/v1.26.md
@@ -12,8 +12,13 @@ on the release branch in GitHub.
 
 ### Important changes:
 
-- OPTIONAL
-- OPTIONAL
+- The `hibernate on/off` commands in the `cnpg` plugin for `kubectl` now serve
+  as shortcuts for the declarative hibernation procedure. The previously
+  available imperative implementation has been removed in favor of the
+  declarative approach. Additionally, the `hibernate status` command has been
+  removed in favor of the standard `status` command.
+  **Do not upgrade to version 1.26 of both the plugin and the operator unless
+  you are prepared to migrate to the declarative method.**
 
 ### Features:
 
diff --git a/internal/cmd/plugin/hibernate/cmd.go b/internal/cmd/plugin/hibernate/cmd.go
index 134c5412..5a2c0c96 100644
--- a/internal/cmd/plugin/hibernate/cmd.go
+++ b/internal/cmd/plugin/hibernate/cmd.go
@@ -17,11 +17,15 @@ limitations under the License.
 package hibernate
 
 import (
+	"context"
 	"fmt"
 
 	"github.com/spf13/cobra"
+	"sigs.k8s.io/controller-runtime/pkg/client"
 
+	apiv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
 	"github.com/cloudnative-pg/cloudnative-pg/internal/cmd/plugin"
+	"github.com/cloudnative-pg/cloudnative-pg/pkg/utils"
 )
 
 var (
@@ -34,17 +38,10 @@ var (
 		},
 		RunE: func(cmd *cobra.Command, args []string) error {
 			clusterName := args[0]
-			force, err := cmd.Flags().GetBool("force")
-			if err != nil {
-				return err
-			}
-
-			hibernateOn, err := newOnCommand(cmd.Context(), clusterName, force)
-			if err != nil {
-				return err
-			}
-
-			return hibernateOn.execute()
+			return annotateCluster(cmd.Context(), plugin.Client, client.ObjectKey{
+				Name:      clusterName,
+				Namespace: plugin.Namespace,
+			}, utils.HibernationAnnotationValueOn)
 		},
 	}
 
@@ -57,34 +54,10 @@ var (
 		},
 		RunE: func(cmd *cobra.Command, args []string) error {
 			clusterName := args[0]
-			off := newOffCommand(cmd.Context(), clusterName)
-			return off.execute()
-		},
-	}
-
-	hibernateStatusCmd = &cobra.Command{
-		Use:   "status CLUSTER",
-		Short: "Prints the hibernation status for the CLUSTER",
-		Args:  plugin.RequiresArguments(1),
-		ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
-			return plugin.CompleteClusters(cmd.Context(), args, toComplete), cobra.ShellCompDirectiveNoFileComp
-		},
-		RunE: func(cmd *cobra.Command, args []string) error {
-			clusterName := args[0]
-			rawOutput, err := cmd.Flags().GetString("output")
-			if err != nil {
-				return err
-			}
-
-			outputFormat := plugin.OutputFormat(rawOutput)
-			switch outputFormat {
-			case plugin.OutputFormatJSON, plugin.OutputFormatYAML:
-				return newStatusCommandStructuredOutput(cmd.Context(), clusterName, outputFormat).execute()
-			case plugin.OutputFormatText:
-				return newStatusCommandTextOutput(cmd.Context(), clusterName).execute()
-			default:
-				return fmt.Errorf("output: %s is not supported by the hibernate CLI", rawOutput)
-			}
+			return annotateCluster(cmd.Context(), plugin.Client, client.ObjectKey{
+				Name:      clusterName,
+				Namespace: plugin.Namespace,
+			}, utils.HibernationAnnotationValueOff)
 		},
 	}
 )
@@ -99,19 +72,37 @@ func NewCmd() *cobra.Command {
 
 	cmd.AddCommand(hibernateOnCmd)
 	cmd.AddCommand(hibernateOffCmd)
-	cmd.AddCommand(hibernateStatusCmd)
-
-	hibernateOnCmd.Flags().Bool(
-		"force",
-		false,
-		"Force the hibernation procedure even if the preconditions are not met")
-	hibernateStatusCmd.Flags().
-		StringP(
-			"output",
-			"o",
-			"text",
-			"Output format. One of text, json, or yaml",
-		)
 
 	return cmd
 }
+
+func annotateCluster(
+	ctx context.Context,
+	cli client.Client,
+	clusterKey client.ObjectKey,
+	value utils.HibernationAnnotationValue,
+) error {
+	var cluster apiv1.Cluster
+
+	if err := cli.Get(ctx, clusterKey, &cluster); err != nil {
+		return fmt.Errorf("failed to get cluster %s: %w", clusterKey.Name, err)
+	}
+
+	if cluster.Annotations == nil {
+		cluster.SetAnnotations(make(map[string]string))
+	}
+
+	origCluster := cluster.DeepCopy()
+
+	cluster.Annotations[utils.HibernationAnnotationName] = string(value)
+
+	if cluster.Annotations[utils.HibernationAnnotationName] == origCluster.Annotations[utils.HibernationAnnotationName] {
+		return fmt.Errorf("cluster %s is already in the requested state", clusterKey.Name)
+	}
+
+	if err := cli.Patch(ctx, &cluster, client.MergeFrom(origCluster)); err != nil {
+		return fmt.Errorf("failed to patch cluster %s: %w", clusterKey.Name, err)
+	}
+
+	return nil
+}
diff --git a/internal/cmd/plugin/hibernate/off.go b/internal/cmd/plugin/hibernate/off.go
deleted file mode 100644
index 2a6e9a23..00000000
--- a/internal/cmd/plugin/hibernate/off.go
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
-Copyright The CloudNativePG Contributors
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package hibernate
-
-import (
-	"context"
-	"fmt"
-
-	"github.com/cloudnative-pg/machinery/pkg/log"
-	corev1 "k8s.io/api/core/v1"
-	apierrs "k8s.io/apimachinery/pkg/api/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
-	"k8s.io/utils/strings/slices"
-
-	apiv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
-	"github.com/cloudnative-pg/cloudnative-pg/internal/cmd/plugin"
-	"github.com/cloudnative-pg/cloudnative-pg/pkg/utils"
-)
-
-// offCommand represent the `hibernate off` command
-type offCommand struct {
-	ctx         context.Context
-	clusterName string
-}
-
-// newOffCommand creates a new `hibernate off` command
-func newOffCommand(ctx context.Context, clusterName string) *offCommand {
-	contextLogger := log.FromContext(ctx).WithValues(
-		"clusterName", clusterName)
-
-	return &offCommand{
-		ctx:         log.IntoContext(ctx, contextLogger),
-		clusterName: clusterName,
-	}
-}
-
-// execute executes the `hibernate off` command
-func (off *offCommand) execute() error {
-	off.printAdvancement("cluster reactivation starting")
-
-	// Ensuring the cluster doesn't exist
-	if err := off.ensureClusterDoesNotExistStep(); err != nil {
-		return err
-	}
-
-	// Get the list of PVC from which we need to resume this cluster
-	pvcGroup, err := getHibernatedPVCGroup(off.ctx, off.clusterName)
-	if err != nil {
-		return err
-	}
-
-	// Ensure the list of PVCs we have is correct
-	if err := off.ensurePVCsArePartOfAPVCGroupStep(pvcGroup); err != nil {
-		return err
-	}
-
-	// We recreate the cluster resource from the first PVC of the group,
-	// and don't care of which PVC we select because we annotate
-	// each PVC of a group with the same data.
-	pvc := pvcGroup[0]
-
-	// We get the original cluster resource from the annotation
-	clusterFromPVC, err := getClusterFromPVCAnnotation(pvc)
-	if err != nil {
-		return err
-	}
-
-	// And recreate it into the Kubernetes cluster
-	if err := off.createClusterWithoutRuntimeDataStep(clusterFromPVC); err != nil {
-		return err
-	}
-
-	off.printAdvancement("cluster reactivation completed")
-
-	return nil
-}
-
-// ensureClusterDoesNotExistStep checks if this cluster exist or not, ensuring
-// that it is not present
-func (off *offCommand) ensureClusterDoesNotExistStep() error {
-	var cluster apiv1.Cluster
-	err := plugin.Client.Get(
-		off.ctx,
-		types.NamespacedName{Name: off.clusterName, Namespace: plugin.Namespace},
-		&cluster,
-	)
-	if err == nil {
-		return fmt.Errorf("cluster already exist, cannot proceed with reactivation")
-	}
-	if !apierrs.IsNotFound(err) {
-		return err
-	}
-	return nil
-}
-
-// ensurePVCsArePartOfAPVCGroupStep check if the passed PVCs are really part of the same group
-func (off *offCommand) ensurePVCsArePartOfAPVCGroupStep(pvcs []corev1.PersistentVolumeClaim) error {
-	// ensure all the pvcs belong to the same node serial and are hibernated
-	var nodeSerial []string
-	for _, pvc := range pvcs {
-		// IMPORTANT: do not use utils.ClusterManifestAnnotationName, utils.PgControlDataAnnotationName here for backwards
-		// compatibility
-		if err := ensureAnnotationsExists(
-			pvc,
-			utils.HibernateClusterManifestAnnotationName,
-			utils.HibernatePgControlDataAnnotationName,
-			utils.ClusterSerialAnnotationName,
-		); err != nil {
-			return err
-		}
-
-		serial := pvc.Annotations[utils.ClusterSerialAnnotationName]
-		if !slices.Contains(nodeSerial, serial) {
-			nodeSerial = append(nodeSerial, serial)
-		}
-	}
-	if len(nodeSerial) != 1 {
-		return fmt.Errorf("hibernate pvcs belong to different instances of the cluster, cannot proceed")
-	}
-
-	return nil
-}
-
-// createClusterWithoutRuntimeDataStep recreate the original cluster back into Kubernetes
-func (off *offCommand) createClusterWithoutRuntimeDataStep(clusterFromPVC apiv1.Cluster) error {
-	cluster := clusterFromPVC.DeepCopy()
-	// remove any runtime kubernetes metadata
-	cluster.ObjectMeta.ResourceVersion = ""
-	cluster.ObjectMeta.ManagedFields = nil
-	cluster.ObjectMeta.UID = ""
-	cluster.ObjectMeta.Generation = 0
-	cluster.ObjectMeta.CreationTimestamp = metav1.Time{}
-	// remove cluster status
-	cluster.Status = apiv1.ClusterStatus{}
-
-	// remove any runtime kubernetes annotations
-	delete(cluster.Annotations, corev1.LastAppliedConfigAnnotation)
-
-	// remove the cluster fencing
-	delete(cluster.Annotations, utils.FencedInstanceAnnotation)
-
-	// create cluster
-	return plugin.Client.Create(off.ctx, cluster)
-}
-
-// ensureAnnotationsExists returns an error if the passed PVC is annotated with all the
-// passed annotations names
-func ensureAnnotationsExists(volume corev1.PersistentVolumeClaim, annotationNames ...string) error {
-	for _, annotationName := range annotationNames {
-		if _, ok := volume.Annotations[annotationName]; !ok {
-			return fmt.Errorf("missing %s annotation, from pvcs: %s", annotationName, volume.Name)
-		}
-	}
-
-	return nil
-}
-
-func (off *offCommand) printAdvancement(msg string) {
-	fmt.Println(msg)
-}
diff --git a/internal/cmd/plugin/hibernate/on.go b/internal/cmd/plugin/hibernate/on.go
deleted file mode 100644
index f9b685b5..00000000
--- a/internal/cmd/plugin/hibernate/on.go
+++ /dev/null
@@ -1,375 +0,0 @@
-/*
-Copyright The CloudNativePG Contributors
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package hibernate
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"strconv"
-	"time"
-
-	"github.com/cloudnative-pg/machinery/pkg/log"
-	corev1 "k8s.io/api/core/v1"
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
-	"k8s.io/apimachinery/pkg/types"
-	"k8s.io/apimachinery/pkg/util/wait"
-	"k8s.io/client-go/util/retry"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-
-	apiv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
-	"github.com/cloudnative-pg/cloudnative-pg/internal/cmd/plugin"
-	"github.com/cloudnative-pg/cloudnative-pg/internal/cmd/plugin/destroy"
-	pluginresources "github.com/cloudnative-pg/cloudnative-pg/internal/plugin/resources"
-	"github.com/cloudnative-pg/cloudnative-pg/pkg/reconciler/persistentvolumeclaim"
-	"github.com/cloudnative-pg/cloudnative-pg/pkg/resources"
-	"github.com/cloudnative-pg/cloudnative-pg/pkg/specs"
-	"github.com/cloudnative-pg/cloudnative-pg/pkg/utils"
-)
-
-var hibernationBackoff = wait.Backoff{
-	Steps:    4,
-	Duration: 10 * time.Second,
-	Factor:   5.0,
-	Jitter:   0.1,
-}
-
-// onCommand represent the `hibernate on` subcommand
-type onCommand struct {
-	ctx                   context.Context
-	cluster               *apiv1.Cluster
-	primaryInstanceSerial int
-	force                 bool
-	shouldRollback        bool
-
-	managedInstances []corev1.Pod
-	primaryInstance  corev1.Pod
-	pvcs             []corev1.PersistentVolumeClaim
-}
-
-// newOnCommand creates a new `hibernate on` command
-func newOnCommand(ctx context.Context, clusterName string, force bool) (*onCommand, error) {
-	var cluster apiv1.Cluster
-
-	// Get the Cluster object
-	err := plugin.Client.Get(
-		ctx,
-		client.ObjectKey{Namespace: plugin.Namespace, Name: clusterName},
-		&cluster)
-	if err != nil {
-		return nil, fmt.Errorf("could not get cluster: %v", err)
-	}
-
-	// Get the instances to be hibernated
-	managedInstances, primaryInstance, err := pluginresources.GetInstancePods(ctx, clusterName)
-	if err != nil {
-		return nil, fmt.Errorf("could not get cluster pods: %w", err)
-	}
-	if primaryInstance.Name == "" {
-		return nil, fmt.Errorf("no primary instance found, cannot proceed with the hibernation")
-	}
-
-	// Get the PVCs that will be hibernated
-	pvcs, err := persistentvolumeclaim.GetInstancePVCs(ctx, plugin.Client, primaryInstance.Name, plugin.Namespace)
-	if err != nil {
-		return nil, fmt.Errorf("cannot get PVCs: %w", err)
-	}
-
-	// Get the serial ID of the primary instance
-	primaryInstanceSerial, err := specs.GetNodeSerial(primaryInstance.ObjectMeta)
-	if err != nil {
-		return nil, fmt.Errorf("could not get the primary node: %w", err)
-	}
-
-	contextLogger := log.FromContext(ctx).WithValues(
-		"clusterName", clusterName,
-		"primaryInstance", primaryInstance.Name)
-
-	return &onCommand{
-		ctx:                   log.IntoContext(ctx, contextLogger),
-		cluster:               &cluster,
-		primaryInstanceSerial: primaryInstanceSerial,
-		managedInstances:      managedInstances,
-		primaryInstance:       primaryInstance,
-		pvcs:                  pvcs,
-		force:                 force,
-		shouldRollback:        false,
-	}, nil
-}
-
-// execute executes the `hibernate on` command
-func (on *onCommand) execute() error {
-	// Check the hibernation preconditions
-	if err := on.checkPreconditionsStep(); err != nil {
-		return err
-	}
-
-	on.printAdvancement("hibernation process starting...")
-
-	if err := on.fenceClusterStep(); err != nil {
-		on.shouldRollback = true
-		return err
-	}
-	defer on.rollbackFenceClusterIfNeeded()
-
-	on.printAdvancement("waiting for the cluster to be fenced")
-
-	if err := on.waitInstancesToBeFencedStep(); err != nil {
-		on.shouldRollback = true
-		return err
-	}
-
-	on.printAdvancement("cluster is now fenced, storing primary pg_controldata output")
-
-	if err := on.annotatePVCStep(); err != nil {
-		on.shouldRollback = true
-		return err
-	}
-	defer on.rollBackAnnotationsIfNeeded()
-
-	on.printAdvancement("PVC annotation complete")
-
-	if err := on.deleteResourcesStep(); err != nil {
-		on.shouldRollback = true
-		return err
-	}
-
-	on.printAdvancement("Hibernation completed")
-	return nil
-}
-
-// checkPreconditionsStep checks if the preconditions for the execution of this step are
-// met or not. If they are not met, it will return an error
-func (on *onCommand) checkPreconditionsStep() error {
-	contextLogger := log.FromContext(on.ctx)
-
-	// We should refuse to hibernate a cluster that was fenced already
-	fencedInstances, err := utils.GetFencedInstances(on.cluster.Annotations)
-	if err != nil {
-		return fmt.Errorf("could not check if cluster is fenced: %v", err)
-	}
-
-	if fencedInstances.Len() > 0 {
-		if on.force {
-			contextLogger.Warning("Continuing hibernation procedure even if there are fenced instances")
-		} else {
-			return fmt.Errorf("cannot hibernate a cluster that has fenced instances")
-		}
-	}
-
-	return nil
-}
-
-func (on *onCommand) fenceClusterStep() error {
-	contextLogger := log.FromContext(on.ctx)
-
-	contextLogger.Debug("applying the fencing annotation to the cluster manifest")
-	if err := utils.NewFencingMetadataExecutor(plugin.Client).
-		AddFencing().
-		ForAllInstances().
-		Execute(
-			on.ctx,
-			types.NamespacedName{Name: on.cluster.Name, Namespace: plugin.Namespace},
-			&apiv1.Cluster{},
-		); err != nil {
-		return err
-	}
-	contextLogger.Debug("fencing annotation set on the cluster manifest")
-
-	return nil
-}
-
-// rollbackFenceClusterIfNeeded removes the fencing status from the
-// cluster
-func (on *onCommand) rollbackFenceClusterIfNeeded() {
-	if !on.shouldRollback {
-		return
-	}
-
-	contextLogger := log.FromContext(on.ctx)
-
-	fmt.Println("rolling back hibernation: removing the fencing annotation")
-	if err := utils.NewFencingMetadataExecutor(plugin.Client).
-		RemoveFencing().
-		ForAllInstances().
-		Execute(on.ctx,
-			types.NamespacedName{Name: on.cluster.Name, Namespace: plugin.Namespace}, &apiv1.Cluster{}); err != nil {
-		contextLogger.Error(err, "Rolling back from hibernation failed")
-	}
-}
-
-// waitInstancesToBeFenced waits for all instances to be shut down
-func (on *onCommand) waitInstancesToBeFencedStep() error {
-	isRetryable := func(err error) bool {
-		return !apierrors.IsForbidden(err) && !apierrors.IsUnauthorized(err)
-	}
-	for _, instance := range on.managedInstances {
-		if err := retry.OnError(hibernationBackoff, isRetryable, func() error {
-			running, err := pluginresources.IsInstanceRunning(on.ctx, instance)
-			if err != nil {
-				return fmt.Errorf("error checking instance status (%v): %w", instance.Name, err)
-			}
-			if running {
-				return fmt.Errorf("instance still running (%v)", instance.Name)
-			}
-			return nil
-		}); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-// annotatePVCStep stores the pg_controldata output
-// into an annotation of the primary PVC
-func (on *onCommand) annotatePVCStep() error {
-	controlData, err := plugin.GetPGControlData(on.ctx, on.primaryInstance)
-	if err != nil {
-		return fmt.Errorf("could not get primary control data: %w", err)
-	}
-	on.printAdvancement("primary pg_controldata output fetched")
-
-	on.printAdvancement("annotating the PVC with the cluster manifest")
-	if err := annotatePVCs(on.ctx, on.pvcs, on.cluster, controlData); err != nil {
-		return fmt.Errorf("could not annotate PVCs: %w", err)
-	}
-
-	return nil
-}
-
-func (on *onCommand) rollBackAnnotationsIfNeeded() {
-	if !on.shouldRollback {
-		return
-	}
-
-	fmt.Println("rolling back hibernation: removing pvc annotations")
-	err := removePVCannotations(on.ctx, on.pvcs)
-	if err != nil {
-		fmt.Printf("could not remove PVC annotations: %v", err)
-	}
-}
-
-func (on *onCommand) deleteResourcesStep() error {
-	on.printAdvancement("destroying the primary instance while preserving the pvc")
-
-	// from this point there is no going back
-	if err := destroy.Destroy(
-		on.ctx,
-		on.cluster.Name,
-		fmt.Sprintf("%s-%s", on.cluster.Name, strconv.Itoa(on.primaryInstanceSerial)),
-		true,
-	); err != nil {
-		return fmt.Errorf("error destroying primary instance: %w", err)
-	}
-	on.printAdvancement("primary instance destroy completed")
-
-	on.printAdvancement("deleting the cluster resource")
-	if err := plugin.Client.Delete(on.ctx, on.cluster); err != nil {
-		return fmt.Errorf("error while deleting cluster resource: %w", err)
-	}
-	on.printAdvancement("cluster resource deletion complete")
-
-	return nil
-}
-
-func (on *onCommand) printAdvancement(msg string) {
-	fmt.Println(msg)
-}
-
-func annotatePVCs(
-	ctx context.Context,
-	pvcs []corev1.PersistentVolumeClaim,
-	cluster *apiv1.Cluster,
-	pgControlData string,
-) error {
-	for _, pvc := range pvcs {
-		if err := retry.OnError(retry.DefaultBackoff, resources.RetryAlways, func() error {
-			var currentPVC corev1.PersistentVolumeClaim
-			if err := plugin.Client.Get(
-				ctx,
-				types.NamespacedName{Name: pvc.Name, Namespace: pvc.Namespace},
-				&currentPVC,
-			); err != nil {
-				return err
-			}
-
-			if currentPVC.Annotations == nil {
-				currentPVC.Annotations = map[string]string{}
-			}
-			origPVC := currentPVC.DeepCopy()
-
-			// IMPORTANT: do not use utils.ClusterManifestAnnotationName, utils.PgControlDataAnnotationName here for backwards
-			// compatibility
-			_, hasHibernateAnnotation := currentPVC.Annotations[utils.HibernateClusterManifestAnnotationName]
-			_, hasPgControlDataAnnotation := currentPVC.Annotations[utils.HibernatePgControlDataAnnotationName]
-			if hasHibernateAnnotation || hasPgControlDataAnnotation {
-				return fmt.Errorf("the PVC already contains Hibernation annotations. Erroring out")
-			}
-
-			bytes, err := json.Marshal(&cluster)
-			if err != nil {
-				return err
-			}
-
-			currentPVC.Annotations[utils.HibernateClusterManifestAnnotationName] = string(bytes)
-			currentPVC.Annotations[utils.HibernatePgControlDataAnnotationName] = pgControlData
-			currentPVC.Annotations[utils.ClusterManifestAnnotationName] = string(bytes)
-			currentPVC.Annotations[utils.PgControldataAnnotationName] = pgControlData
-
-			return plugin.Client.Patch(ctx, &currentPVC, client.MergeFrom(origPVC))
-		}); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-func removePVCannotations(
-	ctx context.Context,
-	pvcs []corev1.PersistentVolumeClaim,
-) error {
-	for _, pvc := range pvcs {
-		if err := retry.OnError(retry.DefaultBackoff, resources.RetryAlways, func() error {
-			var currentPVC corev1.PersistentVolumeClaim
-			if err := plugin.Client.Get(
-				ctx,
-				types.NamespacedName{Name: pvc.Name, Namespace: pvc.Namespace},
-				&currentPVC,
-			); err != nil {
-				return err
-			}
-
-			if currentPVC.Annotations == nil {
-				return nil
-			}
-			origPVC := currentPVC.DeepCopy()
-
-			delete(currentPVC.Annotations, utils.HibernateClusterManifestAnnotationName)
-			delete(currentPVC.Annotations, utils.HibernatePgControlDataAnnotationName)
-			delete(currentPVC.Annotations, utils.ClusterManifestAnnotationName)
-			delete(currentPVC.Annotations, utils.PgControldataAnnotationName)
-
-			return plugin.Client.Patch(ctx, &currentPVC, client.MergeFrom(origPVC))
-		}); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
diff --git a/internal/cmd/plugin/hibernate/output.go b/internal/cmd/plugin/hibernate/output.go
deleted file mode 100644
index d337f5e0..00000000
--- a/internal/cmd/plugin/hibernate/output.go
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
-Copyright The CloudNativePG Contributors
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package hibernate
-
-import (
-	"bytes"
-	"context"
-	"fmt"
-	"os"
-	"strings"
-	"text/tabwriter"
-
-	"github.com/cheynewallace/tabby"
-	"github.com/cloudnative-pg/machinery/pkg/log"
-	"github.com/logrusorgru/aurora/v4"
-	corev1 "k8s.io/api/core/v1"
-	"sigs.k8s.io/yaml"
-
-	apiv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
-	"github.com/cloudnative-pg/cloudnative-pg/internal/cmd/plugin"
-	"github.com/cloudnative-pg/cloudnative-pg/pkg/utils"
-)
-
-const (
-	// statusClusterManifestNotFound is an error message reported when no cluster manifest is not found
-	statusClusterManifestNotFound = "Cluster manifest not found"
-)
-
-// statusOutputManager is a type capable of executing a status output request
-type statusOutputManager interface {
-	addHibernationSummaryInformation(level statusLevel, statusMessage, clusterName string)
-	addClusterManifestInformation(cluster *apiv1.Cluster)
-	addPVCGroupInformation(pvcs []corev1.PersistentVolumeClaim)
-	// execute renders the output
-	execute() error
-}
-
-type textStatusOutputManager struct {
-	textPrinter  *tabby.Tabby
-	stdoutBuffer *bytes.Buffer
-}
-
-func newTextStatusOutputManager() *textStatusOutputManager {
-	buffer := &bytes.Buffer{}
-	return &textStatusOutputManager{
-		textPrinter:  tabby.NewCustom(tabwriter.NewWriter(buffer, 0, 0, 4, ' ', 0)),
-		stdoutBuffer: buffer,
-	}
-}
-
-func (t *textStatusOutputManager) getColor(level statusLevel) aurora.Color {
-	switch level {
-	case warningLevel:
-		return aurora.YellowFg
-	case errorLevel:
-		return aurora.RedFg
-	default:
-		return aurora.GreenFg
-	}
-}
-
-func (t *textStatusOutputManager) addHibernationSummaryInformation(
-	level statusLevel,
-	statusMessage string,
-	clusterName string,
-) {
-	t.textPrinter.AddHeader(aurora.Colorize("Hibernation Summary", t.getColor(level)))
-	t.textPrinter.AddLine("Hibernation status", statusMessage)
-	t.textPrinter.AddLine("Cluster Name", clusterName)
-	t.textPrinter.AddLine("Cluster Namespace", plugin.Namespace)
-	t.textPrinter.AddLine()
-}
-
-func (t *textStatusOutputManager) addClusterManifestInformation(
-	cluster *apiv1.Cluster,
-) {
-	if cluster == nil {
-		t.textPrinter.AddHeader(aurora.Red("Cluster Spec Information"))
-		t.textPrinter.AddLine(aurora.Red(statusClusterManifestNotFound))
-		return
-	}
-
-	t.textPrinter.AddHeader(aurora.Green("Cluster Spec Information"))
-	bytesArray, err := yaml.Marshal(cluster.Spec)
-	if err != nil {
-		const message = "Could not serialize the cluster manifest"
-		t.textPrinter.AddLine(aurora.Red(message))
-		return
-	}
-
-	t.textPrinter.AddLine(string(bytesArray))
-	t.textPrinter.AddLine()
-}
-
-func (t *textStatusOutputManager) addPVCGroupInformation(
-	pvcs []corev1.PersistentVolumeClaim,
-) {
-	if len(pvcs) == 0 {
-		return
-	}
-
-	// there is no need to iterate the pvc group, it is either all valid or none
-	value, ok := pvcs[0].Annotations[utils.HibernatePgControlDataAnnotationName]
-	if !ok {
-		return
-	}
-
-	t.textPrinter.AddHeader(aurora.Green("PostgreSQL instance control information"))
-	t.textPrinter.AddLine(value)
-}
-
-func (t *textStatusOutputManager) execute() error {
-	// do not remove this is to flush the writer cache into the buffer
-	t.textPrinter.Print()
-	fmt.Print(t.stdoutBuffer)
-	return nil
-}
-
-type structuredStatusOutputManager struct {
-	mapToSerialize map[string]interface{}
-	format         plugin.OutputFormat
-	ctx            context.Context
-}
-
-func newStructuredOutputManager(ctx context.Context, format plugin.OutputFormat) *structuredStatusOutputManager {
-	return &structuredStatusOutputManager{
-		mapToSerialize: map[string]interface{}{},
-		format:         format,
-		ctx:            ctx,
-	}
-}
-
-func (t *structuredStatusOutputManager) addHibernationSummaryInformation(
-	level statusLevel,
-	statusMessage string,
-	clusterName string,
-) {
-	t.mapToSerialize["summary"] = map[string]string{
-		"status":      statusMessage,
-		"clusterName": clusterName,
-		"namespace":   plugin.Namespace,
-		"level":       string(level),
-	}
-}
-
-func (t *structuredStatusOutputManager) addClusterManifestInformation(
-	cluster *apiv1.Cluster,
-) {
-	tmpMap := map[string]interface{}{}
-	defer func() {
-		t.mapToSerialize["cluster"] = tmpMap
-	}()
-
-	if cluster == nil {
-		tmpMap["error"] = statusClusterManifestNotFound
-		return
-	}
-
-	tmpMap["spec"] = cluster.Spec
-}
-
-func (t *structuredStatusOutputManager) addPVCGroupInformation(
-	pvcs []corev1.PersistentVolumeClaim,
-) {
-	contextLogger := log.FromContext(t.ctx)
-
-	// there is no need to iterate the pvc group, it is either all valid or none
-	value, ok := pvcs[0].Annotations[utils.HibernatePgControlDataAnnotationName]
-	if !ok {
-		return
-	}
-
-	tmp := map[string]string{}
-	rows := strings.Split(value, "\n")
-	for _, row := range rows {
-		// skip empty rows
-		if strings.Trim(row, " ") == "" {
-			continue
-		}
-
-		res := strings.SplitN(row, ":", 2)
-		if len(res) != 2 {
-			// bad row parsing, we skip it
-			contextLogger.Warning("skipping row because it was malformed", "row", row)
-			tmp["error"] = "one or more rows could not be parsed"
-			continue
-		}
-		tmp[res[0]] = strings.Trim(res[1], " ")
-	}
-
-	t.mapToSerialize["pgControlData"] = tmp
-}
-
-func (t *structuredStatusOutputManager) execute() error {
-	return plugin.Print(t.mapToSerialize, t.format, os.Stdout)
-}
diff --git a/internal/cmd/plugin/hibernate/resources.go b/internal/cmd/plugin/hibernate/resources.go
deleted file mode 100644
index f8deae24..00000000
--- a/internal/cmd/plugin/hibernate/resources.go
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
-Copyright The CloudNativePG Contributors
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package hibernate
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-
-	corev1 "k8s.io/api/core/v1"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-
-	apiv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
-	"github.com/cloudnative-pg/cloudnative-pg/internal/cmd/plugin"
-	"github.com/cloudnative-pg/cloudnative-pg/pkg/utils"
-)
-
-// errNoHibernatedPVCsFound indicates that no PVCs were found.
-var errNoHibernatedPVCsFound = fmt.Errorf("no hibernated PVCs to reactivate found")
-
-// getHibernatedPVCGroupStep gets the PVC group resulting from the hibernation process
-func getHibernatedPVCGroup(ctx context.Context, clusterName string) ([]corev1.PersistentVolumeClaim, error) {
-	// Get the list of PVCs belonging to this group
-	var pvcList corev1.PersistentVolumeClaimList
-	if err := plugin.Client.List(
-		ctx,
-		&pvcList,
-		client.MatchingLabels{utils.ClusterLabelName: clusterName},
-		client.InNamespace(plugin.Namespace),
-	); err != nil {
-		return nil, err
-	}
-	if len(pvcList.Items) == 0 {
-		return nil, errNoHibernatedPVCsFound
-	}
-
-	return pvcList.Items, nil
-}
-
-// getClusterFromPVCAnnotation reads the original cluster resource from the chosen PVC
-func getClusterFromPVCAnnotation(pvc corev1.PersistentVolumeClaim) (apiv1.Cluster, error) {
-	var clusterFromPVC apiv1.Cluster
-	// get the cluster manifest
-	clusterJSON := pvc.Annotations[utils.HibernateClusterManifestAnnotationName]
-	if err := json.Unmarshal([]byte(clusterJSON), &clusterFromPVC); err != nil {
-		return apiv1.Cluster{}, err
-	}
-	return clusterFromPVC, nil
-}
diff --git a/internal/cmd/plugin/hibernate/status.go b/internal/cmd/plugin/hibernate/status.go
deleted file mode 100644
index f425d859..00000000
--- a/internal/cmd/plugin/hibernate/status.go
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
-Copyright The CloudNativePG Contributors
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package hibernate
-
-import (
-	"context"
-	"errors"
-	"fmt"
-
-	corev1 "k8s.io/api/core/v1"
-	apierrs "k8s.io/apimachinery/pkg/api/errors"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-
-	apiv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
-	"github.com/cloudnative-pg/cloudnative-pg/internal/cmd/plugin"
-)
-
-// statusLevel describes if the output should communicate an ok,warning or error status
-type statusLevel string
-
-const (
-	okLevel      statusLevel = "ok"
-	warningLevel statusLevel = "warning"
-	errorLevel   statusLevel = "error"
-)
-
-type statusCommand struct {
-	outputManager statusOutputManager
-	ctx           context.Context
-	clusterName   string
-}
-
-func newStatusCommandStructuredOutput(
-	ctx context.Context,
-	clusterName string,
-	format plugin.OutputFormat,
-) *statusCommand {
-	return &statusCommand{
-		outputManager: newStructuredOutputManager(ctx, format),
-		ctx:           ctx,
-		clusterName:   clusterName,
-	}
-}
-
-func newStatusCommandTextOutput(ctx context.Context, clusterName string) *statusCommand {
-	return &statusCommand{
-		outputManager: newTextStatusOutputManager(),
-		ctx:           ctx,
-		clusterName:   clusterName,
-	}
-}
-
-func (cmd *statusCommand) execute() error {
-	isDeployed, err := cmd.isClusterDeployed()
-	if err != nil {
-		return err
-	}
-	if isDeployed {
-		return cmd.clusterIsAlreadyRunningOutput()
-	}
-
-	pvcs, err := getHibernatedPVCGroup(cmd.ctx, cmd.clusterName)
-	if errors.Is(err, errNoHibernatedPVCsFound) {
-		return cmd.noHibernatedPVCsOutput()
-	}
-	if err != nil {
-		return err
-	}
-
-	return cmd.clusterHibernatedOutput(pvcs)
-}
-
-func (cmd *statusCommand) clusterHibernatedOutput(pvcs []corev1.PersistentVolumeClaim) error {
-	clusterFromPVC, err := getClusterFromPVCAnnotation(pvcs[0])
-	if err != nil {
-		return err
-	}
-
-	cmd.outputManager.addHibernationSummaryInformation(okLevel, "Cluster Hibernated", cmd.clusterName)
-	cmd.outputManager.addClusterManifestInformation(&clusterFromPVC)
-	cmd.outputManager.addPVCGroupInformation(pvcs)
-
-	return cmd.outputManager.execute()
-}
-
-func (cmd *statusCommand) clusterIsAlreadyRunningOutput() error {
-	cmd.outputManager.addHibernationSummaryInformation(warningLevel, "No Hibernation. Cluster Deployed.", cmd.clusterName)
-	return cmd.outputManager.execute()
-}
-
-func (cmd *statusCommand) noHibernatedPVCsOutput() error {
-	cmd.outputManager.addHibernationSummaryInformation(errorLevel, "No hibernated PVCs found", cmd.clusterName)
-	return cmd.outputManager.execute()
-}
-
-func (cmd *statusCommand) isClusterDeployed() (bool, error) {
-	var cluster apiv1.Cluster
-
-	// Get the Cluster object
-	err := plugin.Client.Get(cmd.ctx, client.ObjectKey{Namespace: plugin.Namespace, Name: cmd.clusterName}, &cluster)
-	if apierrs.IsNotFound(err) {
-		return false, nil
-	}
-	if err != nil {
-		return false, fmt.Errorf("error while fetching the cluster resource: %w", err)
-	}
-
-	return true, nil
-}
diff --git a/internal/cmd/plugin/status/status.go b/internal/cmd/plugin/status/status.go
index 4d076220..890362c8 100644
--- a/internal/cmd/plugin/status/status.go
+++ b/internal/cmd/plugin/status/status.go
@@ -120,6 +120,8 @@ func Status(
 	}
 
 	status := extractPostgresqlStatus(ctx, cluster)
+	hibernated, _ := isHibernated(status)
+
 	err = plugin.Print(status, format, os.Stdout)
 	if err != nil || format != plugin.OutputFormatText {
 		return err
@@ -134,16 +136,18 @@ func Status(
 		errs = append(errs, status.printPostgresConfiguration(ctx, clientInterface)...)
 		status.printCertificatesStatus()
 	}
-	status.printBackupStatus()
-	status.printBasebackupStatus(verbosity)
-	status.printReplicaStatus(verbosity)
-	if verbosity > 0 {
-		status.printUnmanagedReplicationSlotStatus()
-		status.printRoleManagerStatus()
-		status.printTablespacesStatus()
-		status.printPodDisruptionBudgetStatus()
+	if !hibernated {
+		status.printBackupStatus()
+		status.printBasebackupStatus(verbosity)
+		status.printReplicaStatus(verbosity)
+		if verbosity > 0 {
+			status.printUnmanagedReplicationSlotStatus()
+			status.printRoleManagerStatus()
+			status.printTablespacesStatus()
+			status.printPodDisruptionBudgetStatus()
+		}
+		status.printInstancesStatus()
 	}
-	status.printInstancesStatus()
 	status.printPluginStatus(verbosity)
 
 	if len(errs) > 0 {
@@ -234,17 +238,10 @@ func (fullStatus *PostgresqlStatus) printBasicInfo(ctx context.Context, k8sClien
 
 	cluster := fullStatus.Cluster
 
-	if cluster.IsReplica() {
-		fmt.Println(aurora.Yellow("Replica Cluster Summary"))
-	} else {
-		fmt.Println(aurora.Green("Cluster Summary"))
-	}
-
 	primaryInstance := cluster.Status.CurrentPrimary
-	if cluster.Status.CurrentPrimary != cluster.Status.TargetPrimary {
-		primaryInstance = fmt.Sprintf("%v (switching to %v)",
-			cluster.Status.CurrentPrimary, cluster.Status.TargetPrimary)
-	}
+
+	// Determine if the cluster is hibernated
+	hibernated, _ := isHibernated(fullStatus)
 
 	fencedInstances, err := utils.GetFencedInstances(cluster.Annotations)
 	if err != nil {
@@ -253,6 +250,11 @@ func (fullStatus *PostgresqlStatus) printBasicInfo(ctx context.Context, k8sClien
 	isPrimaryFenced := cluster.IsInstanceFenced(cluster.Status.CurrentPrimary)
 	primaryInstanceStatus := fullStatus.tryGetPrimaryInstance()
 
+	if cluster.Status.CurrentPrimary != cluster.Status.TargetPrimary {
+		primaryInstance = fmt.Sprintf("%v (switching to %v)",
+			cluster.Status.CurrentPrimary, cluster.Status.TargetPrimary)
+	}
+
 	summary.AddLine("Name", client.ObjectKeyFromObject(cluster).String())
 
 	if primaryInstanceStatus != nil {
@@ -266,12 +268,20 @@ func (fullStatus *PostgresqlStatus) printBasicInfo(ctx context.Context, k8sClien
 		summary.AddLine("Primary instance:", primaryInstance)
 	}
 
-	primaryStartTime := getPrimaryStartTime(cluster)
-	if len(primaryStartTime) > 0 {
-		summary.AddLine("Primary start time:", primaryStartTime)
+	switch {
+	case hibernated:
+		summary.AddLine("Status:", aurora.Red("Hibernated"))
+	case isPrimaryFenced:
+		summary.AddLine("Status:", aurora.Red("Primary instance is fenced"))
+	default:
+		// Avoid printing the start time when hibernated or fenced
+		primaryStartTime := getPrimaryStartTime(cluster)
+		if len(primaryStartTime) > 0 {
+			summary.AddLine("Primary start time:", primaryStartTime)
+		}
+		summary.AddLine("Status:", fullStatus.getStatus(cluster))
 	}
 
-	summary.AddLine("Status:", fullStatus.getStatus(isPrimaryFenced, cluster))
 	if cluster.Spec.Instances == cluster.Status.Instances {
 		summary.AddLine("Instances:", aurora.Green(cluster.Spec.Instances))
 	} else {
@@ -291,16 +301,15 @@ func (fullStatus *PostgresqlStatus) printBasicInfo(ctx context.Context, k8sClien
 		}
 	}
 
-	if cluster.Status.CurrentPrimary != cluster.Status.TargetPrimary {
-		if cluster.Status.CurrentPrimary == "" {
-			fmt.Println(aurora.Red("Primary server is initializing"))
-		} else {
-			fmt.Println(aurora.Red("Switchover in progress"))
-		}
-	}
-
 	if clusterSizeErr != nil {
-		summary.AddLine("Size:", aurora.Red(clusterSizeErr.Error()))
+		switch {
+		case hibernated:
+			summary.AddLine("Size:", "- (hibernated)")
+		case isPrimaryFenced:
+			summary.AddLine("Size:", "- (fenced)")
+		default:
+			summary.AddLine("Size:", aurora.Red(clusterSizeErr.Error()))
+		}
 	} else {
 		summary.AddLine("Size:", clusterSize)
 	}
@@ -315,23 +324,32 @@ func (fullStatus *PostgresqlStatus) printBasicInfo(ctx context.Context, k8sClien
 		summary.AddLine("Current Write LSN:", lsnInfo)
 	}
 
+	if cluster.IsReplica() {
+		fmt.Println(aurora.Yellow("Replica Cluster Summary"))
+	} else {
+		fmt.Println(aurora.Green("Cluster Summary"))
+	}
+
+	if cluster.Status.CurrentPrimary != cluster.Status.TargetPrimary {
+		if cluster.Status.CurrentPrimary == "" {
+			fmt.Println(aurora.Red("Primary server is initializing"))
+		} else {
+			fmt.Println(aurora.Red("Switchover in progress"))
+		}
+	}
+
 	summary.Print()
 	fmt.Println()
 }
 
 func (fullStatus *PostgresqlStatus) printHibernationInfo() {
-	cluster := fullStatus.Cluster
-
-	hibernationCondition := meta.FindStatusCondition(
-		cluster.Status.Conditions,
-		hibernation.HibernationConditionType,
-	)
+	hibernated, hibernationCondition := isHibernated(fullStatus)
 	if hibernationCondition == nil {
 		return
 	}
 
 	hibernationStatus := tabby.New()
-	if hibernationCondition.Status == metav1.ConditionTrue {
+	if hibernated {
 		hibernationStatus.AddLine("Status", "Hibernated")
 	} else {
 		hibernationStatus.AddLine("Status", "Active")
@@ -345,6 +363,20 @@ func (fullStatus *PostgresqlStatus) printHibernationInfo() {
 	fmt.Println()
 }
 
+func isHibernated(fullStatus *PostgresqlStatus) (bool, *metav1.Condition) {
+	cluster := fullStatus.Cluster
+	hibernationCondition := meta.FindStatusCondition(
+		cluster.Status.Conditions,
+		hibernation.HibernationConditionType,
+	)
+
+	if hibernationCondition == nil || hibernationCondition.Status != metav1.ConditionTrue {
+		return false, hibernationCondition
+	}
+
+	return true, hibernationCondition
+}
+
 func (fullStatus *PostgresqlStatus) printTokenStatus(token string) {
 	primaryInstanceStatus := fullStatus.tryGetPrimaryInstance()
 
@@ -428,11 +460,7 @@ func (fullStatus *PostgresqlStatus) printPromotionTokenInfo() {
 	fmt.Println()
 }
 
-func (fullStatus *PostgresqlStatus) getStatus(isPrimaryFenced bool, cluster *apiv1.Cluster) string {
-	if isPrimaryFenced {
-		return fmt.Sprintf("%v", aurora.Red("Primary instance is fenced"))
-	}
-
+func (fullStatus *PostgresqlStatus) getStatus(cluster *apiv1.Cluster) string {
 	switch cluster.Status.Phase {
 	case apiv1.PhaseHealthy, apiv1.PhaseFirstPrimary, apiv1.PhaseCreatingReplica:
 		return fmt.Sprintf("%v %v", aurora.Green(cluster.Status.Phase), cluster.Status.PhaseReason)



Test patch:
diff --git a/internal/cmd/plugin/hibernate/cmd_test.go b/internal/cmd/plugin/hibernate/cmd_test.go
new file mode 100644
index 00000000..89f82bd8
--- /dev/null
+++ b/internal/cmd/plugin/hibernate/cmd_test.go
@@ -0,0 +1,97 @@
+package hibernate
+
+import (
+	"fmt"
+
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	k8client "sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+	apiv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
+	schemeBuilder "github.com/cloudnative-pg/cloudnative-pg/internal/scheme"
+	"github.com/cloudnative-pg/cloudnative-pg/pkg/utils"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+)
+
+var _ = Describe("annotateCluster", func() {
+	var (
+		cluster    *apiv1.Cluster
+		cli        k8client.Client
+		clusterKey k8client.ObjectKey
+	)
+
+	BeforeEach(func() {
+		cluster = &apiv1.Cluster{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "test-cluster",
+				Namespace: "test-namespace",
+			},
+		}
+		cli = fake.NewClientBuilder().WithScheme(schemeBuilder.BuildWithAllKnownScheme()).WithObjects(cluster).Build()
+		clusterKey = k8client.ObjectKeyFromObject(cluster)
+	})
+
+	It("annotates the cluster with hibernation on", func(ctx SpecContext) {
+		err := annotateCluster(ctx, cli, clusterKey, utils.HibernationAnnotationValueOn)
+		Expect(err).ToNot(HaveOccurred())
+
+		updatedCluster := &apiv1.Cluster{}
+		err = cli.Get(ctx, clusterKey, updatedCluster)
+		Expect(err).ToNot(HaveOccurred())
+		Expect(updatedCluster.Annotations[utils.HibernationAnnotationName]).
+			To(Equal(string(utils.HibernationAnnotationValueOn)))
+	})
+
+	It("annotates the cluster with hibernation off", func(ctx SpecContext) {
+		err := annotateCluster(ctx, cli, clusterKey, utils.HibernationAnnotationValueOff)
+		Expect(err).ToNot(HaveOccurred())
+
+		updatedCluster := &apiv1.Cluster{}
+		err = cli.Get(ctx, clusterKey, updatedCluster)
+		Expect(err).ToNot(HaveOccurred())
+		Expect(updatedCluster.Annotations[utils.HibernationAnnotationName]).
+			To(Equal(string(utils.HibernationAnnotationValueOff)))
+	})
+
+	It("returns an error if the cluster is already in the requested state", func(ctx SpecContext) {
+		err := annotateCluster(ctx, cli, clusterKey, utils.HibernationAnnotationValueOn)
+		Expect(err).ToNot(HaveOccurred())
+
+		err = annotateCluster(ctx, cli, clusterKey, utils.HibernationAnnotationValueOn)
+		Expect(err).To(HaveOccurred())
+		Expect(err.Error()).To(Equal(fmt.Sprintf("cluster %s is already in the requested state", clusterKey.Name)))
+	})
+
+	It("returns an error if the cluster cannot be retrieved", func(ctx SpecContext) {
+		nonExistingClusterKey := k8client.ObjectKey{
+			Name:      "non-existing-cluster",
+			Namespace: "test-namespace",
+		}
+
+		err := annotateCluster(ctx, cli, nonExistingClusterKey, utils.HibernationAnnotationValueOn)
+		Expect(err).To(HaveOccurred())
+		Expect(err.Error()).To(ContainSubstring(fmt.Sprintf("failed to get cluster %s", nonExistingClusterKey.Name)))
+	})
+
+	It("toggles hibernation from on to off", func(ctx SpecContext) {
+		err := annotateCluster(ctx, cli, clusterKey, utils.HibernationAnnotationValueOn)
+		Expect(err).ToNot(HaveOccurred())
+
+		updatedCluster := &apiv1.Cluster{}
+		err = cli.Get(ctx, clusterKey, updatedCluster)
+		Expect(err).ToNot(HaveOccurred())
+		Expect(updatedCluster.Annotations[utils.HibernationAnnotationName]).
+			To(Equal(string(utils.HibernationAnnotationValueOn)))
+
+		err = annotateCluster(ctx, cli, clusterKey, utils.HibernationAnnotationValueOff)
+		Expect(err).ToNot(HaveOccurred())
+
+		updatedCluster = &apiv1.Cluster{}
+		err = cli.Get(ctx, clusterKey, updatedCluster)
+		Expect(err).ToNot(HaveOccurred())
+		Expect(updatedCluster.Annotations[utils.HibernationAnnotationName]).
+			To(Equal(string(utils.HibernationAnnotationValueOff)))
+	})
+})
diff --git a/internal/cmd/plugin/hibernate/suite_test.go b/internal/cmd/plugin/hibernate/suite_test.go
new file mode 100644
index 00000000..b87a96a2
--- /dev/null
+++ b/internal/cmd/plugin/hibernate/suite_test.go
@@ -0,0 +1,13 @@
+package hibernate_test
+
+import (
+	"testing"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+)
+
+func TestHibernate(t *testing.T) {
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "Hibernate Suite")
+}
diff --git a/tests/e2e/hibernation_test.go b/tests/e2e/hibernation_test.go
deleted file mode 100644
index 0b4df387..00000000
--- a/tests/e2e/hibernation_test.go
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
-Copyright The CloudNativePG Contributors
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package e2e
-
-import (
-	"encoding/json"
-	"fmt"
-	"strings"
-
-	corev1 "k8s.io/api/core/v1"
-	v1 "k8s.io/api/rbac/v1"
-	"k8s.io/apimachinery/pkg/types"
-	ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
-
-	apiv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
-	"github.com/cloudnative-pg/cloudnative-pg/pkg/reconciler/persistentvolumeclaim"
-	"github.com/cloudnative-pg/cloudnative-pg/pkg/utils"
-	"github.com/cloudnative-pg/cloudnative-pg/tests"
-	"github.com/cloudnative-pg/cloudnative-pg/tests/utils/clusterutils"
-	"github.com/cloudnative-pg/cloudnative-pg/tests/utils/objects"
-	"github.com/cloudnative-pg/cloudnative-pg/tests/utils/postgres"
-	"github.com/cloudnative-pg/cloudnative-pg/tests/utils/run"
-	"github.com/cloudnative-pg/cloudnative-pg/tests/utils/storage"
-	"github.com/cloudnative-pg/cloudnative-pg/tests/utils/timeouts"
-	"github.com/cloudnative-pg/cloudnative-pg/tests/utils/yaml"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-)
-
-var _ = Describe("Cluster Hibernation with plugin", Label(tests.LabelPlugin), func() {
-	type mode string
-	type hibernateSatusMessage string
-	type expectedKeysInStatus string
-	const (
-		sampleFileClusterWithPGWalVolume    = fixturesDir + "/base/cluster-storage-class.yaml.template"
-		sampleFileClusterWithOutPGWalVolume = fixturesDir + "/hibernate/" +
-			"cluster-storage-class-without-wal.yaml.template"
-		level                                         = tests.Medium
-		HibernateOn             mode                  = "on"
-		HibernateOff            mode                  = "off"
-		HibernateStatus         mode                  = "status"
-		clusterOffStatusMessage hibernateSatusMessage = "No Hibernation. Cluster Deployed."
-		clusterOnStatusMessage  hibernateSatusMessage = "Cluster Hibernated"
-		summaryInStatus         expectedKeysInStatus  = "summary"
-		tableName                                     = "test"
-	)
-	BeforeEach(func() {
-		if testLevelEnv.Depth < int(level) {
-			Skip("Test depth is lower than the amount requested for this test")
-		}
-	})
-
-	Context("hibernate", func() {
-		var namespace string
-		var err error
-		getPrimaryAndClusterManifest := func(namespace, clusterName string) ([]byte, string) {
-			var beforeHibernationClusterInfo *apiv1.Cluster
-			var clusterManifest []byte
-			var beforeHibernationCurrentPrimary string
-			By("collecting current primary details", func() {
-				beforeHibernationClusterInfo, err = clusterutils.Get(env.Ctx, env.Client, namespace, clusterName)
-				Expect(err).ToNot(HaveOccurred())
-				beforeHibernationCurrentPrimary = beforeHibernationClusterInfo.Status.CurrentPrimary
-				// collect expected cluster manifesto info
-				clusterManifest, err = json.Marshal(&beforeHibernationClusterInfo)
-				Expect(err).ToNot(HaveOccurred())
-			})
-			return clusterManifest, beforeHibernationCurrentPrimary
-		}
-		getPvc := func(role persistentvolumeclaim.Meta, instanceName string) corev1.PersistentVolumeClaim {
-			pvcName := role.GetName(instanceName)
-			pvcInfo := corev1.PersistentVolumeClaim{}
-			err = objects.Get(env.Ctx, env.Client, ctrlclient.ObjectKey{Namespace: namespace, Name: pvcName}, &pvcInfo)
-			Expect(err).ToNot(HaveOccurred())
-			return pvcInfo
-		}
-		performHibernation := func(mode mode, namespace, clusterName string) {
-			By(fmt.Sprintf("performing hibernation %v", mode), func() {
-				_, _, err := run.Run(fmt.Sprintf("kubectl cnpg hibernate %v %v -n %v",
-					mode, clusterName, namespace))
-				Expect(err).ToNot(HaveOccurred())
-			})
-			By(fmt.Sprintf("verifying cluster %v pods are removed", clusterName), func() {
-				Eventually(func(g Gomega) {
-					podList, _ := clusterutils.ListPods(env.Ctx, env.Client, namespace, clusterName)
-					g.Expect(len(podList.Items)).Should(BeEquivalentTo(0))
-				}, 300).Should(Succeed())
-			})
-		}
-
-		getHibernationStatusInJSON := func(namespace, clusterName string) map[string]interface{} {
-			var data map[string]interface{}
-			By("getting hibernation status", func() {
-				stdOut, _, err := run.Run(fmt.Sprintf("kubectl cnpg hibernate %v %v -n %v -ojson",
-					HibernateStatus, clusterName, namespace))
-				Expect(err).ToNot(HaveOccurred(), stdOut)
-				err = json.Unmarshal([]byte(stdOut), &data)
-				Expect(err).ToNot(HaveOccurred())
-			})
-			return data
-		}
-
-		verifySummaryInHibernationStatus := func(clusterName string, message hibernateSatusMessage) {
-			statusOut := getHibernationStatusInJSON(namespace, clusterName)
-			actualStatus := statusOut[string(summaryInStatus)].(map[string]interface{})["status"].(string)
-			Expect(strings.Contains(string(message), actualStatus)).Should(BeEquivalentTo(true),
-				actualStatus+"\\not-contained-in\\"+string(message))
-		}
-		verifyClusterResources := func(
-			namespace, clusterName string, objs []persistentvolumeclaim.ExpectedObjectCalculator,
-		) {
-			By(fmt.Sprintf("verifying cluster resources are removed "+
-				"post hibernation where roles %v", objs), func() {
-				timeout := 120
-
-				By(fmt.Sprintf("verifying cluster %v is removed", clusterName), func() {
-					Eventually(func() (bool, apiv1.Cluster) {
-						cluster, err := clusterutils.Get(env.Ctx, env.Client, namespace, clusterName)
-						if err != nil {
-							return true, apiv1.Cluster{}
-						}
-						return false, *cluster
-					}, timeout).Should(BeTrue())
-				})
-
-				By(fmt.Sprintf("verifying cluster %v PVCs are removed", clusterName), func() {
-					Eventually(func() (int, error) {
-						pvcList, err := storage.GetPVCList(env.Ctx, env.Client, namespace)
-						if err != nil {
-							return -1, err
-						}
-						return len(pvcList.Items), nil
-					}, timeout).Should(BeEquivalentTo(len(objs)))
-				})
-
-				By(fmt.Sprintf("verifying cluster %v configMap is removed", clusterName), func() {
-					Eventually(func() (bool, corev1.ConfigMap) {
-						configMap := corev1.ConfigMap{}
-						err = env.Client.Get(env.Ctx,
-							ctrlclient.ObjectKey{Namespace: namespace, Name: apiv1.DefaultMonitoringConfigMapName},
-							&configMap)
-						if err != nil {
-							return true, corev1.ConfigMap{}
-						}
-						return false, configMap
-					}, timeout).Should(BeTrue())
-				})
-
-				By(fmt.Sprintf("verifying cluster %v secrets are removed", clusterName), func() {
-					Eventually(func() (bool, corev1.SecretList, error) {
-						secretList := corev1.SecretList{}
-						err = env.Client.List(env.Ctx, &secretList, ctrlclient.InNamespace(namespace))
-						if err != nil {
-							return false, corev1.SecretList{}, err
-						}
-						var getClusterSecrets []string
-						for _, secret := range secretList.Items {
-							if strings.HasPrefix(secret.GetName(), clusterName) {
-								getClusterSecrets = append(getClusterSecrets, secret.GetName())
-							}
-						}
-						if len(getClusterSecrets) == 0 {
-							return true, corev1.SecretList{}, nil
-						}
-						return false, secretList, nil
-					}, timeout).Should(BeTrue())
-				})
-
-				By(fmt.Sprintf("verifying cluster %v role is removed", clusterName), func() {
-					Eventually(func() (bool, v1.Role) {
-						role := v1.Role{}
-						err = env.Client.Get(env.Ctx,
-							ctrlclient.ObjectKey{Namespace: namespace, Name: clusterName},
-							&role)
-						if err != nil {
-							return true, v1.Role{}
-						}
-						return false, role
-					}, timeout).Should(BeTrue())
-				})
-
-				By(fmt.Sprintf("verifying cluster %v rolebinding is removed", clusterName), func() {
-					Eventually(func() (bool, v1.RoleBinding) {
-						roleBinding := v1.RoleBinding{}
-						err = env.Client.Get(env.Ctx,
-							ctrlclient.ObjectKey{Namespace: namespace, Name: clusterName},
-							&roleBinding)
-						if err != nil {
-							return true, v1.RoleBinding{}
-						}
-						return false, roleBinding
-					}, timeout).Should(BeTrue())
-				})
-			})
-		}
-		verifyPvc := func(
-			expectedObject persistentvolumeclaim.ExpectedObjectCalculator, pvcUid types.UID,
-			clusterManifest []byte, instanceName string,
-		) {
-			pvcInfo := getPvc(expectedObject, instanceName)
-			Expect(pvcUid).Should(BeEquivalentTo(pvcInfo.GetUID()))
-			// pvc should be attached annotation with pgControlData and Cluster manifesto
-			expectedAnnotationKeyPresent := []string{
-				utils.HibernatePgControlDataAnnotationName,
-				utils.HibernateClusterManifestAnnotationName,
-				utils.PgControldataAnnotationName,
-				utils.ClusterManifestAnnotationName,
-			}
-			storage.ObjectHasAnnotations(&pvcInfo, expectedAnnotationKeyPresent)
-			expectedAnnotation := map[string]string{
-				utils.HibernateClusterManifestAnnotationName: string(clusterManifest),
-				utils.ClusterManifestAnnotationName:          string(clusterManifest),
-			}
-			storage.ObjectMatchesAnnotations(&pvcInfo, expectedAnnotation)
-		}
-
-		assertHibernation := func(namespace, clusterName, tableName string) {
-			var beforeHibernationPgWalPvcUID types.UID
-			var beforeHibernationPgDataPvcUID types.UID
-
-			// Write a table and some data on the "app" database
-			tableLocator := TableLocator{
-				Namespace:    namespace,
-				ClusterName:  clusterName,
-				DatabaseName: postgres.AppDBName,
-				TableName:    tableName,
-			}
-			AssertCreateTestData(env, tableLocator)
-			clusterManifest, currentPrimary := getPrimaryAndClusterManifest(namespace, clusterName)
-
-			By("collecting pgWal pvc details of current primary", func() {
-				pvcInfo := getPvc(persistentvolumeclaim.NewPgWalCalculator(), currentPrimary)
-				beforeHibernationPgWalPvcUID = pvcInfo.GetUID()
-			})
-
-			By("collecting pgData pvc details of current primary", func() {
-				pvcInfo := getPvc(persistentvolumeclaim.NewPgDataCalculator(), currentPrimary)
-				beforeHibernationPgDataPvcUID = pvcInfo.GetUID()
-			})
-
-			By(fmt.Sprintf("verifying hibernation status"+
-				" before hibernate on cluster %v", clusterName), func() {
-				verifySummaryInHibernationStatus(clusterName, clusterOffStatusMessage)
-			})
-
-			performHibernation(HibernateOn, namespace, clusterName)
-
-			By(fmt.Sprintf("verifying hibernation status"+
-				" after hibernate on cluster %v", clusterName), func() {
-				verifySummaryInHibernationStatus(clusterName, clusterOnStatusMessage)
-			})
-
-			// After hibernation, it will destroy all the resources generated by the cluster,
-			// except the PVCs that belong to the PostgreSQL primary instance.
-			verifyClusterResources(
-				namespace,
-				clusterName,
-				[]persistentvolumeclaim.ExpectedObjectCalculator{
-					persistentvolumeclaim.NewPgWalCalculator(),
-					persistentvolumeclaim.NewPgDataCalculator(),
-				},
-			)
-
-			By("verifying primary pgWal pvc info", func() {
-				verifyPvc(
-					persistentvolumeclaim.NewPgWalCalculator(),
-					beforeHibernationPgWalPvcUID,
-					clusterManifest,
-					currentPrimary,
-				)
-			})
-
-			By("verifying primary pgData pvc info", func() {
-				verifyPvc(
-					persistentvolumeclaim.NewPgDataCalculator(),
-					beforeHibernationPgDataPvcUID,
-					clusterManifest,
-					currentPrimary,
-				)
-			})
-
-			// verifying hibernation off
-			performHibernation(HibernateOff, namespace, clusterName)
-
-			By(fmt.Sprintf("verifying hibernation status after "+
-				"perform hibernation off on cluster %v", clusterName), func() {
-				verifySummaryInHibernationStatus(clusterName, clusterOffStatusMessage)
-			})
-
-			AssertClusterIsReady(namespace, clusterName, testTimeouts[timeouts.ClusterIsReady], env)
-			// Test data should be present after hibernation off
-			AssertDataExpectedCount(env, tableLocator, 2)
-		}
-
-		When("cluster setup with PG-WAL volume", func() {
-			It("hibernation process should work", func() {
-				const namespacePrefix = "hibernation-on-with-pg-wal"
-				clusterName, err := yaml.GetResourceNameFromYAML(env.Scheme, sampleFileClusterWithPGWalVolume)
-				Expect(err).ToNot(HaveOccurred())
-				// Create a cluster in a namespace we'll delete after the test
-				namespace, err = env.CreateUniqueTestNamespace(env.Ctx, env.Client, namespacePrefix)
-				Expect(err).ToNot(HaveOccurred())
-				AssertCreateCluster(namespace, clusterName, sampleFileClusterWithPGWalVolume, env)
-				assertHibernation(namespace, clusterName, tableName)
-			})
-		})
-		When("cluster setup without PG-WAL volume", func() {
-			It("hibernation process should work", func() {
-				var beforeHibernationPgDataPvcUID types.UID
-
-				const namespacePrefix = "hibernation-without-pg-wal"
-				clusterName, err := yaml.GetResourceNameFromYAML(env.Scheme, sampleFileClusterWithOutPGWalVolume)
-				Expect(err).ToNot(HaveOccurred())
-				// Create a cluster in a namespace we'll delete after the test
-				namespace, err = env.CreateUniqueTestNamespace(env.Ctx, env.Client, namespacePrefix)
-				Expect(err).ToNot(HaveOccurred())
-				AssertCreateCluster(namespace, clusterName, sampleFileClusterWithOutPGWalVolume, env)
-				// Write a table and some data on the "app" database
-				tableLocator := TableLocator{
-					Namespace:    namespace,
-					ClusterName:  clusterName,
-					DatabaseName: postgres.AppDBName,
-					TableName:    tableName,
-				}
-				AssertCreateTestData(env, tableLocator)
-				clusterManifest, currentPrimary := getPrimaryAndClusterManifest(namespace,
-					clusterName)
-
-				By("collecting pgData pvc details of current primary", func() {
-					pvcInfo := getPvc(persistentvolumeclaim.NewPgDataCalculator(), currentPrimary)
-					beforeHibernationPgDataPvcUID = pvcInfo.GetUID()
-				})
-
-				By(fmt.Sprintf("verifying hibernation status"+
-					" before hibernate on cluster %v", clusterName), func() {
-					verifySummaryInHibernationStatus(clusterName, clusterOffStatusMessage)
-				})
-
-				performHibernation(HibernateOn, namespace, clusterName)
-
-				By(fmt.Sprintf("verifying hibernation status"+
-					" after hibernate on cluster %v", clusterName), func() {
-					verifySummaryInHibernationStatus(clusterName, clusterOnStatusMessage)
-				})
-
-				// After hibernation, it will destroy all the resources generated by the cluster,
-				// except the PVCs that belong to the PostgreSQL primary instance.
-				verifyClusterResources(
-					namespace,
-					clusterName,
-					[]persistentvolumeclaim.ExpectedObjectCalculator{persistentvolumeclaim.NewPgDataCalculator()},
-				)
-
-				By("verifying primary pgData pvc info", func() {
-					verifyPvc(
-						persistentvolumeclaim.NewPgDataCalculator(),
-						beforeHibernationPgDataPvcUID,
-						clusterManifest,
-						currentPrimary,
-					)
-				})
-
-				// verifying hibernation off
-				performHibernation(HibernateOff, namespace, clusterName)
-				By(fmt.Sprintf("verifying hibernation status"+
-					" before hibernate on cluster %v", clusterName), func() {
-					verifySummaryInHibernationStatus(clusterName, clusterOffStatusMessage)
-				})
-
-				AssertClusterIsReady(namespace, clusterName, testTimeouts[timeouts.ClusterIsReady], env)
-				// Test data should be present after hibernation off
-				AssertDataExpectedCount(env, tableLocator, 2)
-			})
-		})
-		When("cluster hibernation after switchover", func() {
-			It("hibernation process should work", func() {
-				const namespacePrefix = "hibernation-with-switchover"
-				clusterName, err := yaml.GetResourceNameFromYAML(env.Scheme, sampleFileClusterWithPGWalVolume)
-				Expect(err).ToNot(HaveOccurred())
-				// Create a cluster in a namespace we'll delete after the test
-				namespace, err = env.CreateUniqueTestNamespace(env.Ctx, env.Client, namespacePrefix)
-				Expect(err).ToNot(HaveOccurred())
-				AssertCreateCluster(namespace, clusterName, sampleFileClusterWithPGWalVolume, env)
-				AssertSwitchover(namespace, clusterName, env)
-				assertHibernation(namespace, clusterName, tableName)
-			})
-		})
-	})
-})
diff --git a/tests/e2e/tablespaces_test.go b/tests/e2e/tablespaces_test.go
index 7a202db7..f3b21772 100644
--- a/tests/e2e/tablespaces_test.go
+++ b/tests/e2e/tablespaces_test.go
@@ -697,10 +697,6 @@ var _ = Describe("Tablespaces tests", Label(tests.LabelTablespaces,
 			})
 		})
 
-		It("can hibernate via plugin a cluster with tablespaces", func() {
-			assertCanHibernateClusterWithTablespaces(namespace, clusterName, hibernateImperatively, 2)
-		})
-
 		It("can hibernate via annotation a cluster with tablespaces", func() {
 			assertCanHibernateClusterWithTablespaces(namespace, clusterName, hibernateDeclaratively, 6)
 		})
@@ -1279,8 +1275,6 @@ type hibernationMethod string
 const (
 	// hibernateDeclaratively it is a keyword to use while fencing on/off the instances using annotation method
 	hibernateDeclaratively hibernationMethod = "annotation"
-	// hibernateImperatively it is a keyword to use while fencing on/off the instances using plugin method
-	hibernateImperatively hibernationMethod = "plugin"
 )
 
 func hibernateOn(
@@ -1291,13 +1285,6 @@ func hibernateOn(
 	method hibernationMethod,
 ) error {
 	switch method {
-	case hibernateImperatively:
-		_, _, err := run.Run(fmt.Sprintf("kubectl cnpg hibernate on %v -n %v",
-			clusterName, namespace))
-		if err != nil {
-			return err
-		}
-		return nil
 	case hibernateDeclaratively:
 		cluster, err := clusterutils.Get(ctx, crudClient, namespace, clusterName)
 		if err != nil {
@@ -1324,13 +1311,6 @@ func hibernateOff(
 	method hibernationMethod,
 ) error {
 	switch method {
-	case hibernateImperatively:
-		_, _, err := run.Run(fmt.Sprintf("kubectl cnpg hibernate off %v -n %v",
-			clusterName, namespace))
-		if err != nil {
-			return err
-		}
-		return nil
 	case hibernateDeclaratively:
 		cluster, err := clusterutils.Get(ctx, crudClient, namespace, clusterName)
 		if err != nil {