Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions api/v1beta2/foundationdbrestore_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ type FoundationDBRestoreSpec struct {
// The path to the encryption key used to encrypt the backup.
// +kubebuilder:validation:MaxLength=4096
EncryptionKeyPath string `json:"encryptionKeyPath,omitempty"`

// Instead of the latest version the backup can be restored to, restore to the specified version.
// +nullable
BackupVersion *uint64 `json:"backupVersion,omitempty"`
}

// FoundationDBRestoreStatus describes the current status of the restore for a cluster.
Expand Down
5 changes: 5 additions & 0 deletions api/v1beta2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ spec:
type: object
spec:
properties:
backupVersion:
format: int64
nullable: true
type: integer
blobStoreConfiguration:
properties:
accountName:
Expand Down
8 changes: 4 additions & 4 deletions controllers/admin_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,10 +364,10 @@ var _ = Describe("admin_client_test", func() {
Expect(
mockAdminClient.StartRestore(
"blobstore://test@test-service/test-backup",
nil,
"",
),
).To(Succeed())
fdbv1beta2.FoundationDBRestore{
Spec: fdbv1beta2.FoundationDBRestoreSpec{
DestinationClusterName: cluster.Name,
}})).To(Succeed())

restoreStatus, err = mockAdminClient.GetRestoreStatus()
Expect(err).NotTo(HaveOccurred())
Expand Down
3 changes: 1 addition & 2 deletions controllers/start_restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ func (s startRestore) reconcile(
if len(strings.TrimSpace(status)) == 0 {
err = adminClient.StartRestore(
restore.BackupURL(),
restore.Spec.KeyRanges,
restore.Spec.EncryptionKeyPath,
*restore,
)
if err != nil {
return &requeue{curError: err}
Expand Down
1 change: 1 addition & 0 deletions docs/restore_spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ FoundationDBRestoreSpec describes the desired state of the backup for a cluster.
| blobStoreConfiguration | This is the configuration of the target blobstore for this backup. | *BlobStoreConfiguration | false |
| customParameters | CustomParameters defines additional parameters to pass to the backup agents. | FoundationDBCustomParameters | false |
| encryptionKeyPath | The path to the encryption key used to encrypt the backup. | string | false |
| backupVersion | Instead of the latest version the backup can be restored to, restore to the specified version. | *uint64 | false |

[Back to TOC](#table-of-contents)

Expand Down
4 changes: 3 additions & 1 deletion e2e/fixtures/fdb_backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,8 @@ func (fdbBackup *FdbBackup) WaitForReconciliation() {
}

// WaitForRestorableVersion will wait until the back is restorable.
func (fdbBackup *FdbBackup) WaitForRestorableVersion(version uint64) {
func (fdbBackup *FdbBackup) WaitForRestorableVersion(version uint64) uint64 {
var restorableVersion uint64
gomega.Eventually(func(g gomega.Gomega) uint64 {
backupPod := fdbBackup.GetBackupPod()
out, _, err := fdbBackup.fdbCluster.ExecuteCmdOnPod(
Expand All @@ -241,6 +242,7 @@ func (fdbBackup *FdbBackup) WaitForRestorableVersion(version uint64) {

return ptr.Deref(status.LatestRestorablePoint.Version, 0)
}).WithTimeout(10*time.Minute).WithPolling(2*time.Second).Should(gomega.BeNumerically(">", version), "error waiting for restorable version")
return restorableVersion
}

// GetBackupPod returns a random backup Pod for the provided backup.
Expand Down
7 changes: 6 additions & 1 deletion e2e/fixtures/fdb_restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import (
// CreateRestoreForCluster will create a FoundationDBRestore resource based on the provided backup resource.
// For more information how the backup system with the operator is working please look at
// the operator documentation: https://github.com/FoundationDB/fdb-kubernetes-operator/v2/blob/master/docs/manual/backup.md
func (factory *Factory) CreateRestoreForCluster(backup *FdbBackup) {
func (factory *Factory) CreateRestoreForCluster(backup *FdbBackup, backupVersion *uint64) {
gomega.Expect(backup).NotTo(gomega.BeNil())
restore := &fdbv1beta2.FoundationDBRestore{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -49,6 +49,11 @@ func (factory *Factory) CreateRestoreForCluster(backup *FdbBackup) {
CustomParameters: backup.backup.Spec.CustomParameters,
},
}

if backupVersion != nil {
restore.Spec.BackupVersion = backupVersion
}

gomega.Expect(factory.CreateIfAbsent(restore)).NotTo(gomega.HaveOccurred())

factory.AddShutdownHook(func() error {
Expand Down
16 changes: 13 additions & 3 deletions e2e/test_operator_backups/operator_backup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ var _ = Describe("Operator Backup", Label("e2e", "pr"), func() {
var keyValues []fixtures.KeyValue
var prefix byte = 'a'
var backup *fixtures.FdbBackup

// Delete the backup resource after each test. Note that this will not delete the data
// in the backup store.
AfterEach(func() {
Expand All @@ -104,7 +103,7 @@ var _ = Describe("Operator Backup", Label("e2e", "pr"), func() {

It("should restore the cluster successfully", func() {
fdbCluster.ClearRange([]byte{prefix}, 60)
factory.CreateRestoreForCluster(backup)
factory.CreateRestoreForCluster(backup, nil)
Expect(fdbCluster.GetRange([]byte{prefix}, 25, 60)).Should(Equal(keyValues))
})
})
Expand Down Expand Up @@ -174,7 +173,18 @@ var _ = Describe("Operator Backup", Label("e2e", "pr"), func() {

It("should restore the cluster successfully", func() {
fdbCluster.ClearRange([]byte{prefix}, 60)
factory.CreateRestoreForCluster(backup)
factory.CreateRestoreForCluster(backup, nil)
Expect(fdbCluster.GetRange([]byte{prefix}, 25, 60)).Should(Equal(keyValues))
})

It("should restore the cluster successfully with a restorable version", func() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this test doesn't belong here? I assume we don't want to run the restore test with the experimental partitioned backup system?

var prefix byte = 'b'
var keyValues = fdbCluster.GenerateRandomValues(10, prefix)
fdbCluster.WriteKeyValues(keyValues)
var restorableVersion = backup.WaitForRestorableVersion(fdbCluster.GetClusterVersion())
backup.Stop()
fdbCluster.ClearRange([]byte{prefix}, 60)
factory.CreateRestoreForCluster(backup, &restorableVersion)
Expect(fdbCluster.GetRange([]byte{prefix}, 25, 60)).Should(Equal(keyValues))
})

Expand Down
15 changes: 9 additions & 6 deletions fdbclient/admin_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -919,8 +919,7 @@ func (client *cliAdminClient) GetBackupStatus() (*fdbv1beta2.FoundationDBLiveBac
// StartRestore starts a new restore.
func (client *cliAdminClient) StartRestore(
url string,
keyRanges []fdbv1beta2.FoundationDBKeyRange,
encryptionKeyPath string,
restore fdbv1beta2.FoundationDBRestore,
) error {
args := []string{
"start",
Expand All @@ -933,13 +932,17 @@ func (client *cliAdminClient) StartRestore(
return verErr
}

if encryptionKeyPath != "" && fdbVersion.SupportsBackupEncryption() {
args = append(args, "--encryption-key-file", encryptionKeyPath)
if restore.Spec.EncryptionKeyPath != "" && fdbVersion.SupportsBackupEncryption() {
args = append(args, "--encryption-key-file", restore.Spec.EncryptionKeyPath)
}

if restore.Spec.BackupVersion != nil {
args = append(args, "-v", strconv.FormatUint(*restore.Spec.BackupVersion, 10))
}

if keyRanges != nil {
if restore.Spec.KeyRanges != nil {
keyRangeString := ""
for _, keyRange := range keyRanges {
for _, keyRange := range restore.Spec.KeyRanges {
if keyRangeString != "" {
keyRangeString += ";"
}
Expand Down
48 changes: 47 additions & 1 deletion fdbclient/admin_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1129,7 +1129,11 @@ protocol fdb00b071010000`,

url := "blobstore://test@test-service/test-backup"

err := client.StartRestore(url, keyRanges, encryptionKeyPath)
err := client.StartRestore(url, fdbv1beta2.FoundationDBRestore{
Spec: fdbv1beta2.FoundationDBRestoreSpec{
KeyRanges: keyRanges,
EncryptionKeyPath: encryptionKeyPath,
}})
Expect(err).NotTo(HaveOccurred())

Expect(mockRunner.receivedArgs[0]).To(ContainElements(
Expand Down Expand Up @@ -1185,4 +1189,46 @@ protocol fdb00b071010000`,
false,
),
)

DescribeTable("it should properly handle backup versions",
func(actualBackupVersion *uint64, expectedBackupVersion string) {
mockRunner := &mockCommandRunner{
mockedError: nil,
mockedOutput: []string{""},
}

client := &cliAdminClient{
Cluster: &fdbv1beta2.FoundationDBCluster{
Spec: fdbv1beta2.FoundationDBClusterSpec{
Version: "7.3.1",
},
Status: fdbv1beta2.FoundationDBClusterStatus{
RunningVersion: "7.3.1",
},
},
log: logr.Discard(),
cmdRunner: mockRunner,
}

url := "blobstore://test@test-service/test-backup"

err := client.StartRestore(url, fdbv1beta2.FoundationDBRestore{
Spec: fdbv1beta2.FoundationDBRestoreSpec{
BackupVersion: actualBackupVersion,
},
})
Expect(err).NotTo(HaveOccurred())

if expectedBackupVersion != "" {
Expect(
mockRunner.receivedArgs[0],
).To(ContainElements("-v", expectedBackupVersion))
} else {
Expect(mockRunner.receivedArgs[0]).ToNot(ContainElement("-v"))
}

},
Entry("when it is passed in", ptr.To(uint64(1234567890123)), "1234567890123"),
Entry("when it is not passed in", nil, ""),
)
})
3 changes: 1 addition & 2 deletions pkg/fdbadminclient/admin_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,7 @@ type AdminClient interface {
// StartRestore starts a new restore.
StartRestore(
url string,
keyRanges []fdbv1beta2.FoundationDBKeyRange,
encyptionKeyPath string,
fdbRestore fdbv1beta2.FoundationDBRestore,
) error

// GetRestoreStatus gets the status of the current restore.
Expand Down
3 changes: 1 addition & 2 deletions pkg/fdbadminclient/mock/admin_client_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -969,8 +969,7 @@ func (client *AdminClient) GetBackupStatus() (*fdbv1beta2.FoundationDBLiveBackup
// StartRestore starts a new restore.
func (client *AdminClient) StartRestore(
url string,
_ []fdbv1beta2.FoundationDBKeyRange,
_ string,
_ fdbv1beta2.FoundationDBRestore,
) error {
adminClientMutex.Lock()
defer adminClientMutex.Unlock()
Expand Down