Skip to content

Commit

Permalink
refactor: improve metadata fencing code organization (cloudnative-pg#…
Browse files Browse the repository at this point in the history
…4152)

Currently, the metadata fencing code is challenging for inexperienced
developers to use, given that it is fragmented across the codebase
without a clear pattern.
    
This patch aggregates the logic and adds a fluent builder to execute the
fencing metadata logic.
    
It also improves handling certain operations like fencing or unfencing an
already present or deleted instance. In those cases, the executor does
not return an error anymore.

Signed-off-by: Armando Ruocco <[email protected]>
Signed-off-by: Leonardo Cecchi <[email protected]>
Co-authored-by: Leonardo Cecchi <[email protected]>
  • Loading branch information
armru and leonardoce authored Apr 10, 2024
1 parent 182157e commit 570787f
Show file tree
Hide file tree
Showing 13 changed files with 197 additions and 197 deletions.
2 changes: 1 addition & 1 deletion api/v1/cluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2998,7 +2998,7 @@ func (cluster *Cluster) IsInstanceFenced(instance string) bool {
return false
}

if fencedInstances.Has(utils.FenceAllServers) {
if fencedInstances.Has(utils.FenceAllInstances) {
return true
}
return fencedInstances.Has(instance)
Expand Down
20 changes: 4 additions & 16 deletions controllers/cluster_restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,22 +104,10 @@ func ensureClusterIsNotFenced(
cli client.Client,
cluster *apiv1.Cluster,
) error {
fencedInstances, err := utils.GetFencedInstances(cluster.Annotations)
if err != nil {
return err
}
if fencedInstances.Len() == 0 {
return nil
}

clusterOrig := cluster.DeepCopy()

// we remove the fenced instances this way to ensure that the patch method will work
if err := utils.RemoveFencedInstance(utils.FenceAllServers, &cluster.ObjectMeta); err != nil {
return err
}

return cli.Patch(ctx, cluster, client.MergeFrom(clusterOrig))
return utils.NewFencingMetadataExecutor(cli).
RemoveFencing().
ForAllInstances().
Execute(ctx, client.ObjectKeyFromObject(cluster), cluster)
}

// restoreClusterStatus bootstraps the status needed to make the restored cluster work
Expand Down
3 changes: 2 additions & 1 deletion controllers/cluster_restore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,9 @@ var _ = Describe("ensureClusterIsNotFenced", func() {

Context("when fenced instances exist", func() {
BeforeEach(func() {
err := utils.AddFencedInstance(utils.FenceAllServers, &cluster.ObjectMeta)
modified, err := utils.AddFencedInstance(utils.FenceAllInstances, &cluster.ObjectMeta)
Expect(err).ToNot(HaveOccurred())
Expect(modified).To(BeTrue())
mockCli = fake.NewClientBuilder().
WithScheme(k8scheme.BuildWithAllKnownScheme()).
WithObjects(cluster).
Expand Down
21 changes: 17 additions & 4 deletions internal/cmd/plugin/fence/fence.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,22 @@ import (
"context"
"fmt"

"k8s.io/apimachinery/pkg/types"

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/resources"
"github.com/cloudnative-pg/cloudnative-pg/pkg/utils"
)

// fencingOn marks an instance in a cluster as fenced
func fencingOn(ctx context.Context, clusterName string, serverName string) error {
err := resources.ApplyFenceFunc(ctx, plugin.Client, clusterName, plugin.Namespace, serverName, utils.AddFencedInstance)
err := utils.NewFencingMetadataExecutor(plugin.Client).
AddFencing().
ForInstance(serverName).
Execute(ctx,
types.NamespacedName{Name: clusterName, Namespace: plugin.Namespace},
&apiv1.Cluster{},
)
if err != nil {
return err
}
Expand All @@ -38,8 +46,13 @@ func fencingOn(ctx context.Context, clusterName string, serverName string) error

// fencingOff marks an instance in a cluster as not fenced
func fencingOff(ctx context.Context, clusterName string, serverName string) error {
err := resources.ApplyFenceFunc(ctx, plugin.Client, clusterName, plugin.Namespace,
serverName, utils.RemoveFencedInstance)
err := utils.NewFencingMetadataExecutor(plugin.Client).
RemoveFencing().
ForInstance(serverName).
Execute(ctx,
types.NamespacedName{Name: clusterName, Namespace: plugin.Namespace},
&apiv1.Cluster{},
)
if err != nil {
return err
}
Expand Down
30 changes: 13 additions & 17 deletions internal/cmd/plugin/hibernate/on.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,14 +177,14 @@ func (on *onCommand) fenceClusterStep() error {
contextLogger := log.FromContext(on.ctx)

contextLogger.Debug("applying the fencing annotation to the cluster manifest")
if err := resources.ApplyFenceFunc(
on.ctx,
plugin.Client,
on.cluster.Name,
plugin.Namespace,
utils.FenceAllServers,
utils.AddFencedInstance,
); err != nil {
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")
Expand All @@ -202,15 +202,11 @@ func (on *onCommand) rollbackFenceClusterIfNeeded() {
contextLogger := log.FromContext(on.ctx)

fmt.Println("rolling back hibernation: removing the fencing annotation")
err := resources.ApplyFenceFunc(
on.ctx,
plugin.Client,
on.cluster.Name,
plugin.Namespace,
utils.FenceAllServers,
utils.RemoveFencedInstance,
)
if err != nil {
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")
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/cmd/plugin/status/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ func ExtractPostgresqlStatus(ctx context.Context, clusterName string) (*Postgres
}

func listFencedInstances(fencedInstances *stringset.Data) string {
if fencedInstances.Has(utils.FenceAllServers) {
if fencedInstances.Has(utils.FenceAllInstances) {
return "All Instances"
}
return strings.Join(fencedInstances.ToList(), ", ")
Expand Down
18 changes: 4 additions & 14 deletions pkg/reconciler/backup/volumesnapshot/offline.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import (

apiv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
"github.com/cloudnative-pg/cloudnative-pg/pkg/management/log"
"github.com/cloudnative-pg/cloudnative-pg/pkg/resources"
"github.com/cloudnative-pg/cloudnative-pg/pkg/utils"
)

Expand Down Expand Up @@ -124,19 +123,10 @@ func (o *offlineExecutor) ensurePodIsFenced(
"targetBackup", backup.Name, "targetPod", targetPodName,
)
}

err = resources.ApplyFenceFunc(
ctx,
o.cli,
cluster.Name,
cluster.Namespace,
targetPodName,
utils.AddFencedInstance,
)
if errors.Is(err, utils.ErrorServerAlreadyFenced) {
return nil
}
if err != nil {
if err = utils.NewFencingMetadataExecutor(o.cli).
AddFencing().
ForInstance(targetPodName).
Execute(ctx, client.ObjectKeyFromObject(cluster), cluster); err != nil {
return err
}

Expand Down
3 changes: 2 additions & 1 deletion pkg/reconciler/backup/volumesnapshot/offline_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ var _ = Describe("offlineExecutor", func() {
})

It("finalize should remove the fencing annotation from the cluster", func(ctx SpecContext) {
err := utils.AddFencedInstance(pod.Name, &cluster.ObjectMeta)
modified, err := utils.AddFencedInstance(pod.Name, &cluster.ObjectMeta)
Expect(err).ToNot(HaveOccurred())
Expect(modified).To(BeTrue())

err = oe.cli.Update(ctx, cluster)
Expect(err).ToNot(HaveOccurred())
Expand Down
18 changes: 4 additions & 14 deletions pkg/reconciler/backup/volumesnapshot/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"strconv"
"time"
Expand All @@ -37,7 +36,6 @@ import (
"github.com/cloudnative-pg/cloudnative-pg/pkg/management/log"
"github.com/cloudnative-pg/cloudnative-pg/pkg/management/postgres"
"github.com/cloudnative-pg/cloudnative-pg/pkg/reconciler/persistentvolumeclaim"
"github.com/cloudnative-pg/cloudnative-pg/pkg/resources"
"github.com/cloudnative-pg/cloudnative-pg/pkg/resources/instance"
"github.com/cloudnative-pg/cloudnative-pg/pkg/utils"
)
Expand Down Expand Up @@ -346,18 +344,10 @@ func EnsurePodIsUnfenced(
) error {
contextLogger := log.FromContext(ctx)

err := resources.ApplyFenceFunc(
ctx,
cli,
cluster.Name,
cluster.Namespace,
targetPod.Name,
utils.RemoveFencedInstance,
)
if errors.Is(err, utils.ErrorServerAlreadyUnfenced) {
return nil
}
if err != nil {
if err := utils.NewFencingMetadataExecutor(cli).
RemoveFencing().
ForInstance(targetPod.Name).
Execute(ctx, client.ObjectKeyFromObject(cluster), cluster); err != nil {
return err
}

Expand Down
68 changes: 0 additions & 68 deletions pkg/resources/fencing.go

This file was deleted.

Loading

0 comments on commit 570787f

Please sign in to comment.