plugin-barman-cloud/test/e2e/internal/tests/backup/backup_restore.go
Marco Nenciarini 1be34fe13e
feat(ip): assign copyright to the Linux Foundation (#571)
Adopt the new attribution information for contributions to
CloudNativePG:

```
Copyright © contributors to CloudNativePG, established as
CloudNativePG a Series of LF Projects, LLC.
```

Adopt the SPDX format for Apache License 2.0

Signed-off-by: Marco Nenciarini <marco.nenciarini@enterprisedb.com>
Signed-off-by: Leonardo Cecchi <leonardo.cecchi@enterprisedb.com>
Co-authored-by: Leonardo Cecchi <leonardo.cecchi@enterprisedb.com>
2025-10-07 18:06:06 +02:00

213 lines
6.6 KiB
Go

/*
Copyright © contributors to CloudNativePG, established as
CloudNativePG a Series of LF Projects, LLC.
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.
SPDX-License-Identifier: Apache-2.0
*/
package backup
import (
"fmt"
"time"
v1 "github.com/cloudnative-pg/api/pkg/api/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
internalClient "github.com/cloudnative-pg/plugin-barman-cloud/test/e2e/internal/client"
internalCluster "github.com/cloudnative-pg/plugin-barman-cloud/test/e2e/internal/cluster"
"github.com/cloudnative-pg/plugin-barman-cloud/test/e2e/internal/command"
nmsp "github.com/cloudnative-pg/plugin-barman-cloud/test/e2e/internal/namespace"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Backup and restore", func() {
var namespace *corev1.Namespace
var cl client.Client
BeforeEach(func(ctx SpecContext) {
var err error
cl, _, err = internalClient.NewClient()
Expect(err).NotTo(HaveOccurred())
namespace, err = nmsp.CreateUniqueNamespace(ctx, cl, "backup-restore")
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func(ctx SpecContext) {
Expect(cl.Delete(ctx, namespace)).To(Succeed())
})
DescribeTable("should backup and restore a cluster",
func(
ctx SpecContext,
factory testCaseFactory,
) {
testResources := factory.createBackupRestoreTestResources(namespace.Name)
By("starting the ObjectStore deployment")
Expect(testResources.ObjectStoreResources.Create(ctx, cl)).To(Succeed())
By("creating the ObjectStore")
Expect(cl.Create(ctx, testResources.ObjectStore)).To(Succeed())
By("Creating a CloudNativePG cluster")
src := testResources.SrcCluster
Expect(cl.Create(ctx, testResources.SrcCluster)).To(Succeed())
By("Having the Cluster ready")
Eventually(func(g Gomega) {
g.Expect(cl.Get(
ctx,
types.NamespacedName{
Name: src.Name,
Namespace: src.Namespace,
},
src)).To(Succeed())
g.Expect(internalCluster.IsReady(*src)).To(BeTrue())
}).WithTimeout(10 * time.Minute).WithPolling(10 * time.Second).Should(Succeed())
By("Adding data to PostgreSQL")
clientSet, cfg, err := internalClient.NewClientSet()
Expect(err).NotTo(HaveOccurred())
_, _, err = command.ExecuteInContainer(ctx,
*clientSet,
cfg,
command.ContainerLocator{
NamespaceName: src.Namespace,
PodName: fmt.Sprintf("%v-1", src.Name),
ContainerName: "postgres",
},
nil,
[]string{"psql", "-tAc", "CREATE TABLE test (i int); INSERT INTO test VALUES (1);"})
Expect(err).NotTo(HaveOccurred())
By("Creating a backup")
backup := testResources.SrcBackup
Expect(cl.Create(ctx, backup)).To(Succeed())
By("Waiting for the backup to complete")
Eventually(func(g Gomega) {
g.Expect(cl.Get(ctx, types.NamespacedName{Name: backup.Name, Namespace: backup.Namespace},
backup)).To(Succeed())
g.Expect(backup.Status.Phase).To(BeEquivalentTo(v1.BackupPhaseCompleted))
}).Within(2 * time.Minute).WithPolling(5 * time.Second).Should(Succeed())
By("Adding data after the backup")
_, _, err = command.ExecuteInContainer(ctx,
*clientSet,
cfg,
command.ContainerLocator{
NamespaceName: src.Namespace,
PodName: fmt.Sprintf("%v-1", src.Name),
ContainerName: "postgres",
},
nil,
[]string{
"psql", "-tAc",
"SELECT pg_switch_wal()" +
"; INSERT INTO test VALUES (2)",
})
Expect(err).NotTo(HaveOccurred())
_, _, err = command.ExecuteInContainer(ctx,
*clientSet,
cfg,
command.ContainerLocator{
NamespaceName: src.Namespace,
PodName: fmt.Sprintf("%v-1", src.Name),
ContainerName: "postgres",
},
nil,
[]string{
"psql", "-tAc",
"SELECT pg_switch_wal()",
})
Expect(err).NotTo(HaveOccurred())
By("Restoring the backup")
dst := testResources.DstCluster
Expect(cl.Create(ctx, dst)).To(Succeed())
By("Having the Cluster ready")
Eventually(func(g Gomega) {
g.Expect(cl.Get(ctx,
types.NamespacedName{Name: dst.Name, Namespace: dst.Namespace},
dst)).To(Succeed())
g.Expect(internalCluster.IsReady(*dst)).To(BeTrue())
}).WithTimeout(10 * time.Minute).WithPolling(10 * time.Second).Should(Succeed())
By("Verifying the data exists in the restored instance")
output, _, err := command.ExecuteInContainer(ctx,
*clientSet,
cfg,
command.ContainerLocator{
NamespaceName: dst.Namespace,
PodName: fmt.Sprintf("%v-1", dst.Name),
ContainerName: "postgres",
},
nil,
[]string{"psql", "-tAc", "SELECT count(*) FROM test;"})
Expect(err).NotTo(HaveOccurred())
Expect(output).To(BeEquivalentTo("2\n"))
By("taking a backup from the restored cluster")
backup = testResources.DstBackup
Expect(cl.Create(ctx, backup)).To(Succeed())
By("Waiting for the backup to complete")
Eventually(func(g Gomega) {
g.Expect(cl.Get(ctx, types.NamespacedName{Name: backup.Name, Namespace: backup.Namespace},
backup)).To(Succeed())
g.Expect(backup.Status.Phase).To(BeEquivalentTo(v1.BackupPhaseCompleted))
}).Within(3 * time.Minute).WithPolling(5 * time.Second).Should(Succeed())
},
Entry(
"using the plugin for backup and restore on S3",
&s3BackupPluginBackupPluginRestore{},
),
Entry(
"using the plugin for backup and in-tree for restore on S3",
&s3BackupPluginBackupInTreeRestore{},
),
Entry(
"using in-tree for backup and the plugin for restore on S3",
&s3BackupPluginInTreeBackupPluginRestore{},
),
Entry(
"using the plugin for backup and restore on Azure",
&azureBackupPluginBackupPluginRestore{},
),
Entry(
"using the plugin for backup and in-tree for restore on Azure",
&azureBackupPluginBackupInTreeRestore{},
),
Entry(
"using in-tree for backup and the plugin for restore on Azure",
&azureBackupPluginInTreeBackupPluginRestore{},
),
Entry("using the plugin for backup and restore on GCS",
&gcsBackupPluginBackupPluginRestore{},
),
Entry("using the plugin for backup and in-tree for restore on GCS",
&gcsBackupPluginBackupInTreeRestore{},
),
Entry(
"using in-tree for backup and the plugin for restore on GCS",
&gcsBackupPluginInTreeBackupPluginRestore{},
),
)
})