Skip to content

Commit ec84a45

Browse files
committed
feat: implement recovery window
Signed-off-by: Leonardo Cecchi <[email protected]>
1 parent 9caa60b commit ec84a45

File tree

6 files changed

+182
-50
lines changed

6 files changed

+182
-50
lines changed

api/v1/objectstore_types.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,21 @@ type ObjectStoreSpec struct {
5454

5555
// ObjectStoreStatus defines the observed state of ObjectStore.
5656
type ObjectStoreStatus struct {
57-
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
58-
// Important: Run "make" to regenerate code after modifying this file
57+
// ServerRecoveryWindow maps each server to its recovery window
58+
ServerRecoveryWindow map[string]RecoveryWindow `json:"serverRecoveryWindow,omitempty"`
59+
}
60+
61+
// RecoveryWindow represents the time span between the first
62+
// recoverability point and the last successful backup of a PostgreSQL
63+
// server, defining the period during which data can be restored.
64+
type RecoveryWindow struct {
65+
// The first recoverability point in a PostgreSQL server refers to
66+
// the earliest point in time to which the database can be
67+
// restored.
68+
FirstRecoverabilityPoint *metav1.Time `json:"firstRecoverabilityPoint,omitempty"`
69+
70+
// The last successful backup time
71+
LastSuccessfulBackupTime *metav1.Time `json:"lastSuccussfulBackupTime,omitempty"`
5972
}
6073

6174
// +kubebuilder:object:root=true

api/v1/zz_generated.deepcopy.go

Lines changed: 31 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/barmancloud.cnpg.io_objectstores.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,29 @@ spec:
520520
type: object
521521
status:
522522
description: ObjectStoreStatus defines the observed state of ObjectStore.
523+
properties:
524+
serverRecoveryWindow:
525+
additionalProperties:
526+
description: |-
527+
RecoveryWindow represents the time span between the first
528+
recoverability point and the last successful backup of a PostgreSQL
529+
server, defining the period during which data can be restored.
530+
properties:
531+
firstRecoverabilityPoint:
532+
description: |-
533+
The first recoverability point in a PostgreSQL server refers to
534+
the earliest point in time to which the database can be
535+
restored.
536+
format: date-time
537+
type: string
538+
lastSuccussfulBackupTime:
539+
description: The last successful backup time
540+
format: date-time
541+
type: string
542+
type: object
543+
description: ServerRecoveryWindow maps each server to its recovery
544+
window
545+
type: object
523546
type: object
524547
required:
525548
- metadata

internal/cnpgi/instance/retention.go

Lines changed: 72 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ import (
77
"slices"
88
"time"
99

10+
"github.com/cloudnative-pg/barman-cloud/pkg/catalog"
1011
barmanCommand "github.com/cloudnative-pg/barman-cloud/pkg/command"
1112
barmanCredentials "github.com/cloudnative-pg/barman-cloud/pkg/credentials"
1213
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
14+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1315
"k8s.io/apimachinery/pkg/types"
1416
"k8s.io/client-go/tools/record"
17+
"k8s.io/utils/ptr"
1518
"sigs.k8s.io/controller-runtime/pkg/client"
1619
"sigs.k8s.io/controller-runtime/pkg/log"
1720

@@ -75,8 +78,6 @@ func (c *RetentionPolicyRunnable) Start(ctx context.Context) error {
7578
// cycle enforces the retention policies. On success, it returns the amount
7679
// of time to wait to the next check.
7780
func (c *RetentionPolicyRunnable) cycle(ctx context.Context) (time.Duration, error) {
78-
contextLogger := log.FromContext(ctx)
79-
8081
var cluster cnpgv1.Cluster
8182
var barmanObjectStore barmancloudv1.ObjectStore
8283

@@ -89,67 +90,118 @@ func (c *RetentionPolicyRunnable) cycle(ctx context.Context) (time.Duration, err
8990
return 0, err
9091
}
9192

92-
retentionPolicy := barmanObjectStore.Spec.RetentionPolicy
93+
if err := c.applyRetentionPolicy(ctx, &cluster, &barmanObjectStore); err != nil {
94+
return 0, err
95+
}
96+
9397
nextCheckInterval := time.Second * time.Duration(
9498
barmanObjectStore.Spec.InstanceSidecarConfiguration.RetentionPolicyIntervalSeconds)
99+
return nextCheckInterval, nil
100+
}
95101

102+
// applyRetentionPolicy applies the retention policy to the object
103+
// store and deletes the stale Kubernetes backup objects.
104+
func (c *RetentionPolicyRunnable) applyRetentionPolicy(
105+
ctx context.Context,
106+
cluster *cnpgv1.Cluster,
107+
objectStore *barmancloudv1.ObjectStore,
108+
) error {
109+
contextLogger := log.FromContext(ctx)
110+
111+
configuration := config.NewFromCluster(cluster)
112+
113+
retentionPolicy := objectStore.Spec.RetentionPolicy
96114
if len(retentionPolicy) == 0 {
97115
contextLogger.Info("Skipping retention policy enforcement, no retention policy specified")
98-
return nextCheckInterval, nil
116+
return nil
99117
}
100118
if cluster.Status.CurrentPrimary != c.PodName {
101119
contextLogger.Info(
102-
"Skipping retention policy enforcement, detected standby",
120+
"Skipping retention policy enforcement, not the current primary",
103121
"currentPrimary", cluster.Status.CurrentPrimary, "podName", c.PodName)
104-
return nextCheckInterval, nil
122+
return nil
105123
}
106124

107-
contextLogger.Info("Enforcing retention policies",
125+
contextLogger.Info("Applying backup retention policy",
108126
"retentionPolicy", retentionPolicy)
109127

110128
osEnvironment := os.Environ()
111-
caBundleEnvironment := common.GetRestoreCABundleEnv(&barmanObjectStore.Spec.Configuration)
129+
caBundleEnvironment := common.GetRestoreCABundleEnv(&objectStore.Spec.Configuration)
112130
env, err := barmanCredentials.EnvSetBackupCloudCredentials(
113131
ctx,
114132
c.Client,
115-
barmanObjectStore.Namespace,
116-
&barmanObjectStore.Spec.Configuration,
133+
objectStore.Namespace,
134+
&objectStore.Spec.Configuration,
117135
common.MergeEnv(osEnvironment, caBundleEnvironment))
118136
if err != nil {
119137
contextLogger.Error(err, "while setting backup cloud credentials")
120-
return 0, err
138+
return err
121139
}
122140

123141
if err := barmanCommand.DeleteBackupsByPolicy(
124142
ctx,
125-
&barmanObjectStore.Spec.Configuration,
143+
&objectStore.Spec.Configuration,
126144
configuration.ServerName,
127145
env,
128146
retentionPolicy,
129147
); err != nil {
130148
contextLogger.Error(err, "while enforcing retention policies")
131-
c.Recorder.Event(&cluster, "Warning", "RetentionPolicyFailed", "Retention policy failed")
132-
return 0, err
149+
c.Recorder.Event(cluster, "Warning", "RetentionPolicyFailed", "Retention policy failed")
150+
return err
133151
}
134152

135153
backupList, err := barmanCommand.GetBackupList(
136154
ctx,
137-
&barmanObjectStore.Spec.Configuration,
155+
&objectStore.Spec.Configuration,
138156
configuration.ServerName,
139157
env,
140158
)
141159
if err != nil {
142160
contextLogger.Error(err, "while reading the backup list")
143-
return 0, err
161+
return err
144162
}
145163

146-
if err := deleteBackupsNotInCatalog(ctx, c.Client, &cluster, backupList.GetBackupIDs()); err != nil {
164+
if err := deleteBackupsNotInCatalog(ctx, c.Client, cluster, backupList.GetBackupIDs()); err != nil {
147165
contextLogger.Error(err, "while deleting Backups not present in the catalog")
148-
return 0, err
166+
return err
149167
}
150168

151-
// TODO(leonardoce): update first point of recoverability
152-
return nextCheckInterval, nil
169+
return c.updateRecoveryWindow(ctx, backupList, objectStore, configuration.ServerName)
170+
}
171+
172+
// updateRecoveryWindow updates the recovery window inside the object
173+
// store status subresource
174+
func (c *RetentionPolicyRunnable) updateRecoveryWindow(
175+
ctx context.Context,
176+
backupList *catalog.Catalog,
177+
objectStore *barmancloudv1.ObjectStore,
178+
serverName string,
179+
) error {
180+
// Set the recovery window inside the barman object store object
181+
convertTime := func(t *time.Time) *metav1.Time {
182+
if t == nil {
183+
return nil
184+
}
185+
186+
return ptr.To(metav1.NewTime(*t))
187+
}
188+
189+
firstRecoverabilityPoint := backupList.GetFirstRecoverabilityPoint()
190+
lastSuccessfulBackupTime := backupList.GetLastSuccessfulBackupTime()
191+
recoveryWindow := barmancloudv1.RecoveryWindow{
192+
FirstRecoverabilityPoint: convertTime(firstRecoverabilityPoint),
193+
LastSuccessfulBackupTime: convertTime(lastSuccessfulBackupTime),
194+
}
195+
196+
if objectStore.Status.ServerRecoveryWindow == nil {
197+
objectStore.Status.ServerRecoveryWindow = make(map[string]barmancloudv1.RecoveryWindow)
198+
}
199+
objectStore.Status.ServerRecoveryWindow[serverName] = recoveryWindow
200+
if err := c.Client.Status().Update(ctx, objectStore); err != nil {
201+
return err
202+
}
203+
204+
return nil
153205
}
154206

155207
// deleteBackupsNotInCatalog deletes all Backup objects pointing to the given cluster that are not

internal/cnpgi/operator/lifecycle.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ func reconcilePodSpec(
309309
},
310310
{
311311
Name: "CUSTOM_CNPG_GROUP",
312-
Value: "postgresql.cnpg.io",
312+
Value: "postgresql.k8s.enterprisedb.io",
313313
},
314314
{
315315
Name: "CUSTOM_CNPG_VERSION",

internal/cnpgi/operator/specs/role.go

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -35,35 +35,49 @@ func BuildRole(
3535
}
3636
}
3737

38-
role.Rules = append(role.Rules, rbacv1.PolicyRule{
39-
APIGroups: []string{
40-
"barmancloud.cnpg.io",
41-
},
42-
Verbs: []string{
43-
"get",
44-
"watch",
45-
"list",
46-
},
47-
Resources: []string{
48-
"objectstores",
49-
},
50-
ResourceNames: barmanObjectsSet.ToSortedList(),
51-
})
52-
53-
role.Rules = append(role.Rules, rbacv1.PolicyRule{
54-
APIGroups: []string{
55-
"",
38+
role.Rules = append(
39+
role.Rules,
40+
rbacv1.PolicyRule{
41+
APIGroups: []string{
42+
"barmancloud.cnpg.io",
43+
},
44+
Verbs: []string{
45+
"get",
46+
"watch",
47+
"list",
48+
},
49+
Resources: []string{
50+
"objectstores",
51+
},
52+
ResourceNames: barmanObjectsSet.ToSortedList(),
5653
},
57-
Resources: []string{
58-
"secrets",
54+
rbacv1.PolicyRule{
55+
APIGroups: []string{
56+
"barmancloud.cnpg.io",
57+
},
58+
Verbs: []string{
59+
"update",
60+
},
61+
Resources: []string{
62+
"objectstores/status",
63+
},
64+
ResourceNames: barmanObjectsSet.ToSortedList(),
5965
},
60-
Verbs: []string{
61-
"get",
62-
"watch",
63-
"list",
66+
rbacv1.PolicyRule{
67+
APIGroups: []string{
68+
"",
69+
},
70+
Resources: []string{
71+
"secrets",
72+
},
73+
Verbs: []string{
74+
"get",
75+
"watch",
76+
"list",
77+
},
78+
ResourceNames: secretsSet.ToSortedList(),
6479
},
65-
ResourceNames: secretsSet.ToSortedList(),
66-
})
80+
)
6781

6882
return role
6983
}

0 commit comments

Comments
 (0)