Skip to content

Commit

Permalink
Add backup opt-in/opt-out E2E test
Browse files Browse the repository at this point in the history
Signed-off-by: danfengl <[email protected]>
  • Loading branch information
danfengliu committed Sep 13, 2022
1 parent be40d7e commit bda5469
Show file tree
Hide file tree
Showing 11 changed files with 441 additions and 14 deletions.
7 changes: 3 additions & 4 deletions test/e2e/backups/ttl.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,11 @@ func (b *TTL) Init() {
}

func TTLTest() {
var err error
useVolumeSnapshots := true
test := new(TTL)
client, err := NewTestClient(VeleroCfg.DefaultCluster)
if err != nil {
println(err.Error())
}
client := *VeleroCfg.ClientToInstallVelero

//Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup tests")

BeforeEach(func() {
Expand Down
5 changes: 5 additions & 0 deletions test/e2e/e2e_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
. "github.com/vmware-tanzu/velero/test/e2e/bsl-mgmt"
. "github.com/vmware-tanzu/velero/test/e2e/orderedresources"
. "github.com/vmware-tanzu/velero/test/e2e/privilegesmgmt"
. "github.com/vmware-tanzu/velero/test/e2e/pv-backup"
. "github.com/vmware-tanzu/velero/test/e2e/resource-filtering"
. "github.com/vmware-tanzu/velero/test/e2e/scale"
. "github.com/vmware-tanzu/velero/test/e2e/upgrade"
Expand Down Expand Up @@ -118,13 +119,17 @@ var _ = Describe("[BSL][Deletion][Snapshot] Local backups will be deleted once t
var _ = Describe("[BSL][Deletion][Restic] Local backups and restic repos will be deleted once the corresponding backup storage location is deleted", BslDeletionWithRestic)

var _ = Describe("[Migration][Restic]", MigrationWithRestic)

var _ = Describe("[Migration][Snapshot]", MigrationWithSnapshots)

var _ = Describe("[Schedule][OrederedResources] Backup resources should follow the specific order in schedule", ScheduleOrderedResources)

var _ = Describe("[NamespaceMapping][Single] Backup resources should follow the specific order in schedule", OneNamespaceMappingTest)
var _ = Describe("[NamespaceMapping][Multiple] Backup resources should follow the specific order in schedule", MultiNamespacesMappingTest)

var _ = Describe("[pv-backup][Opt-In] Backup resources should follow the specific order in schedule", OptInPVBackupTest)
var _ = Describe("[pv-backup][Opt-Out] Backup resources should follow the specific order in schedule", OptOutPVBackupTest)

func GetKubeconfigContext() error {
var err error
var tcDefault, tcStandby TestClient
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/migration/migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func MigrationTest(useVolumeSnapshots bool, veleroCLI2Version VeleroCLI2Version)
DeleteNamespace(context.Background(), *VeleroCfg.StandbyClient, migrationNamespace, true)
})
}
By(fmt.Sprintf("Switch to default kubeconfig context %s", VeleroCfg.DefaultCluster), func() {
By(fmt.Sprintf("Switch to default kubeconfig context %s", VeleroCfg.DefaultClient), func() {
Expect(KubectlConfigUseContext(context.Background(), VeleroCfg.DefaultCluster)).To(Succeed())
VeleroCfg.ClientToInstallVelero = VeleroCfg.DefaultClient
})
Expand Down
6 changes: 2 additions & 4 deletions test/e2e/orderedresources/ordered_resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,8 @@ func ScheduleOrderedResources() {
func (o *OrderedResources) Init() error {
rand.Seed(time.Now().UnixNano())
UUIDgen, _ = uuid.NewRandom()
client, err := NewTestClient(VeleroCfg.DefaultCluster)
if err != nil {
return fmt.Errorf("failed to init ordered resources test with err %v", err)
}
client := *VeleroCfg.ClientToInstallVelero

o.Client = client
o.ScheduleName = "schedule-ordered-resources-" + UUIDgen.String()
o.NSBaseName = "schedule-ordered-resources"
Expand Down
182 changes: 182 additions & 0 deletions test/e2e/pv-backup/pv-backup-filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package basic

import (
"context"
"fmt"
"strings"
"time"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/pkg/errors"

. "github.com/vmware-tanzu/velero/test/e2e"
. "github.com/vmware-tanzu/velero/test/e2e/test"
. "github.com/vmware-tanzu/velero/test/e2e/util/k8s"
)

type PVBackupFiltering struct {
TestCase
annotation string
podsList [][]string
volumesList [][]string
}

const POD_COUNT, VOLUME_COUNT_PER_POD = 2, 3
const OPT_IN_ANN, OPT_OUT_ANN = "backup.velero.io/backup-volumes", "backup.velero.io/backup-volumes-excludes"
const FILE_NAME = "test-data.txt"

var OptInPVBackupTest func() = TestFunc(&PVBackupFiltering{annotation: OPT_IN_ANN})
var OptOutPVBackupTest func() = TestFunc(&PVBackupFiltering{annotation: OPT_OUT_ANN})

func (p *PVBackupFiltering) Init() error {
p.Client = TestClientInstance
p.NSBaseName = "ns"
p.NSIncluded = &[]string{fmt.Sprintf("%s-%d", p.NSBaseName, 1), fmt.Sprintf("%s-%d", p.NSBaseName, 2)}

p.TestMsg = &TestMSG{
Desc: "Backup PV filter by annotation",
FailedMSG: "Failed to backup PV filter by annotation",
Text: fmt.Sprintf("should backup namespaces %s", *p.NSIncluded),
}
return nil
}

func (p *PVBackupFiltering) StartRun() error {
p.BackupName = p.BackupName + "backup-opt-in-" + UUIDgen.String()
p.RestoreName = p.RestoreName + "restore-opt-in-" + UUIDgen.String()
p.BackupArgs = []string{
"create", "--namespace", VeleroCfg.VeleroNamespace, "backup", p.BackupName,
"--include-namespaces", strings.Join(*p.NSIncluded, ","),
"--default-volumes-to-restic", "--snapshot-volumes=false", "--wait",
}
p.RestoreArgs = []string{
"create", "--namespace", VeleroCfg.VeleroNamespace, "restore", p.RestoreName,
"--from-backup", p.BackupName, "--wait",
}
return nil
}
func (p *PVBackupFiltering) CreateResources() error {
p.Ctx, _ = context.WithTimeout(context.Background(), 60*time.Minute)
for _, ns := range *p.NSIncluded {
By(fmt.Sprintf("Create namespaces %s for workload\n", ns), func() {
Expect(CreateNamespace(p.Ctx, p.Client, ns)).To(Succeed(), fmt.Sprintf("Failed to create namespace %s", ns))
})
var pods []string
By(fmt.Sprintf("Deploy more pods with several PVs in namespace %s", ns), func() {
var volumesToAnnotation string
for i := 0; i <= POD_COUNT-1; i++ {
var volumeToAnnotationList []string
var volumes []string
for j := 0; j <= VOLUME_COUNT_PER_POD-1; j++ {
volume := fmt.Sprintf("volume-%d-%d", i, j)
volumes = append(volumes, volume)
//Policy for apply annotation
if j%2 == 0 {
volumeToAnnotationList = append(volumeToAnnotationList, volume)
}
}
p.volumesList = append(p.volumesList, volumes)
volumesToAnnotation = strings.Join(volumeToAnnotationList, ",")
podName := fmt.Sprintf("pod-%d", i)
pods = append(pods, podName)
By(fmt.Sprintf("Create pod %s in namespace %s", podName, ns), func() {
pod, err := CreatePodWithPVC(p.Client, ns, podName, "kibishii-storage-class", volumes)
Expect(err).To(Succeed())
ann := map[string]string{
p.annotation: volumesToAnnotation,
}
By(fmt.Sprintf("Add annotation to pod %s of namespace %s", pod.Name, ns), func() {
_, err := AddAnnotationToPod(p.Ctx, p.Client, ns, pod.Name, ann)
Expect(err).To(Succeed())
})
})
}
})
p.podsList = append(p.podsList, pods)
}
By(fmt.Sprintf("Waiting for all pods to start %s\n", p.podsList), func() {
for index, ns := range *p.NSIncluded {
By(fmt.Sprintf("Waiting for all pods to start %d in namespace %s", index, ns), func() {
WaitForPods(p.Ctx, p.Client, ns, p.podsList[index])
})
}
})
By(fmt.Sprintf("Polulate all pods %s with file %s", p.podsList, FILE_NAME), func() {
for index, ns := range *p.NSIncluded {
By(fmt.Sprintf("Creating file in all pods to start %d in namespace %s", index, ns), func() {
WaitForPods(p.Ctx, p.Client, ns, p.podsList[index])
for i, pod := range p.podsList[index] {
for j, _ := range p.volumesList[i] {
Expect(CreateFileToPod(p.Ctx, ns, pod, p.volumesList[i][j],
FILE_NAME, FileContent(ns, pod, p.volumesList[i][j]))).To(Succeed())
}
}
})
}
})
return nil
}

func (p *PVBackupFiltering) Verify() error {
p.Ctx, _ = context.WithTimeout(context.Background(), 60*time.Minute)
By(fmt.Sprintf("Waiting for all pods to start %s", p.podsList), func() {
for index, ns := range *p.NSIncluded {
By(fmt.Sprintf("Waiting for all pods to start %d in namespace %s", index, ns), func() {
WaitForPods(p.Ctx, p.Client, ns, p.podsList[index])
})
}
})

for k, ns := range *p.NSIncluded {
By("Verify PV backed up according to annotation", func() {
for i := 0; i <= POD_COUNT-1; i++ {
for j := 0; j <= VOLUME_COUNT_PER_POD-1; j++ {
if j%2 == 0 {
if p.annotation == OPT_IN_ANN {
Expect(FileExist(p.Ctx, ns, p.podsList[k][i], p.volumesList[i][j])).To(Succeed())
} else {
Expect(FileNotExist(p.Ctx, ns, p.podsList[k][i], p.volumesList[i][j])).To(Succeed())
}
} else {
if p.annotation == OPT_OUT_ANN {
Expect(FileExist(p.Ctx, ns, p.podsList[k][i], p.volumesList[i][j])).To(Succeed())
} else {
Expect(FileNotExist(p.Ctx, ns, p.podsList[k][i], p.volumesList[i][j])).To(Succeed())
}
}
}
}
})
}

return nil
}
func FileContent(namespace, podName, volume string) string {
return fmt.Sprintf("ns-%s pod-%s volume-%s", namespace, podName, volume)
}

func FileExist(ctx context.Context, namespace, podName, volume string) error {
c, err := ReadFileFromPodVolume(ctx, namespace, podName, volume, FILE_NAME)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("Fail to read file %s from volume %s of pod %s in %s ",
FILE_NAME, volume, podName, namespace))
}
c = strings.Replace(c, "\n", "", -1)
origin_content := strings.Replace(FileContent(namespace, podName, volume), "\n", "", -1)
if c == origin_content {
return nil
} else {
return errors.New(fmt.Sprintf("UNEXPECTED: File %s does not exsit in volume %s of pod %s in namespace %s.",
FILE_NAME, volume, podName, namespace))
}
}
func FileNotExist(ctx context.Context, namespace, podName, volume string) error {
_, err := ReadFileFromPodVolume(ctx, namespace, podName, volume, FILE_NAME)
if err != nil {
return nil
} else {
return errors.New(fmt.Sprintf("UNEXPECTED: File %s exsit in volume %s of pod %s in namespace %s.",
FILE_NAME, volume, podName, namespace))
}
}
10 changes: 6 additions & 4 deletions test/e2e/test/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,12 @@ func (t *TestCase) Destroy() error {
}

func (t *TestCase) Restore() error {
if err := VeleroCmdExec(t.Ctx, VeleroCfg.VeleroCLI, t.RestoreArgs); err != nil {
RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, t.BackupName, "")
return errors.Wrapf(err, "Failed to restore resources")
}
By("Start to restore ......", func() {
Expect(VeleroCmdExec(t.Ctx, VeleroCfg.VeleroCLI, t.RestoreArgs)).To(Succeed(), func() string {
RunDebug(context.Background(), VeleroCfg.VeleroCLI, VeleroCfg.VeleroNamespace, t.BackupName, "")
return "Fail to restore workload"
})
})
return nil
}

Expand Down
65 changes: 65 additions & 0 deletions test/e2e/util/k8s/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

"github.com/pkg/errors"
"golang.org/x/net/context"
corev1 "k8s.io/api/core/v1"
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
Expand Down Expand Up @@ -217,3 +218,67 @@ func GetAPIVersions(client *TestClient, name string) ([]string, error) {
}
return nil, errors.New("Server API groups is empty")
}

func GetPVByPodName(client TestClient, namespace, podName string) (string, error) {
pvcList, err := GetPvcByPodName(context.Background(), namespace, podName)
if err != nil {
return "", err
}
if len(pvcList) != 1 {
return "", errors.New(fmt.Sprintf("Only 1 PVC of pod %s should be found under namespace %s", podName, namespace))
}
pvList, err := GetPvByPvc(context.Background(), namespace, pvcList[0])
if err != nil {
return "", err
}
if len(pvList) != 1 {
return "", errors.New(fmt.Sprintf("Only 1 PV of PVC %s pod %s should be found under namespace %s", pvcList[0], podName, namespace))
}
pv_value, err := GetPersistentVolume(context.Background(), client, "", pvList[0])
fmt.Println(pv_value.Annotations["pv.kubernetes.io/provisioned-by"])
if err != nil {
return "", err
}
return pv_value.Name, nil
}
func CreatePodWithPVC(client TestClient, ns, podName, sc string, volumeNameList []string) (*corev1.Pod, error) {
volumes := []corev1.Volume{}
for _, volume := range volumeNameList {
pvc, err := CreatePVC(client, ns, fmt.Sprintf("pvc-%s", volume), sc)
if err != nil {
return nil, err
}
volumes = append(volumes, corev1.Volume{
Name: volume,
VolumeSource: corev1.VolumeSource{
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
ClaimName: pvc.Name,
ReadOnly: false,
},
},
})
}
pod, err := CreatePod(client, ns, podName, volumes)
if err != nil {
return nil, err
}
return pod, nil
}

func CreateFileToPod(ctx context.Context, namespace, podName, volume, filename, content string) error {
arg := []string{"exec", "-n", namespace, "-c", podName, podName,
"--", "/bin/sh", "-c", fmt.Sprintf("echo ns-%s pod-%s volume-%s > /%s/%s", namespace, podName, volume, volume, filename)}
cmd := exec.CommandContext(ctx, "kubectl", arg...)
fmt.Printf("Kubectl exec cmd =%v\n", cmd)
return cmd.Run()
}
func ReadFileFromPodVolume(ctx context.Context, namespace, podName, volume, filename string) (string, error) {
arg := []string{"exec", "-n", namespace, "-c", podName, podName,
"--", "cat", fmt.Sprintf("/%s/%s", volume, filename)}
cmd := exec.CommandContext(ctx, "kubectl", arg...)
fmt.Printf("Kubectl exec cmd =%v\n", cmd)
stdout, stderr, err := veleroexec.RunCommand(cmd)
fmt.Print(stdout)
fmt.Print(stderr)
return stdout, err
}
2 changes: 1 addition & 1 deletion test/e2e/util/k8s/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func CleanupNamespacesWithPoll(ctx context.Context, client TestClient, nsBaseNam
if err != nil {
return errors.Wrapf(err, "Could not delete namespace %s", checkNamespace.Name)
}
fmt.Printf("Delete namespace %s", checkNamespace.Name)
fmt.Printf("Delete namespace %s\n", checkNamespace.Name)
}
}
return nil
Expand Down
38 changes: 38 additions & 0 deletions test/e2e/util/k8s/persistentvolumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,49 @@ package k8s

import (
"context"
"fmt"

"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"

"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func CreatePersistentVolume(client TestClient, name string) (*corev1.PersistentVolume, error) {

p := &corev1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: corev1.PersistentVolumeSpec{
StorageClassName: "manual",
AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
Capacity: corev1.ResourceList{corev1.ResourceName(corev1.ResourceStorage): resource.MustParse("2Gi")},

PersistentVolumeSource: corev1.PersistentVolumeSource{
HostPath: &corev1.HostPathVolumeSource{
Path: "/demo",
},
},
},
}

return client.ClientGo.CoreV1().PersistentVolumes().Create(context.TODO(), p, metav1.CreateOptions{})
}

func GetPersistentVolume(ctx context.Context, client TestClient, namespace string, persistentVolume string) (*corev1.PersistentVolume, error) {
return client.ClientGo.CoreV1().PersistentVolumes().Get(ctx, persistentVolume, metav1.GetOptions{})
}

func AddAnnotationToPersistentVolume(ctx context.Context, client TestClient, namespace string, persistentVolume, key string) (*corev1.PersistentVolume, error) {
newPV, err := GetPersistentVolume(ctx, client, "", persistentVolume)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("Fail to ge PV %s", persistentVolume))
}
ann := newPV.ObjectMeta.Annotations
ann[key] = persistentVolume
newPV.Annotations = ann

return client.ClientGo.CoreV1().PersistentVolumes().Update(ctx, newPV, metav1.UpdateOptions{})
}
Loading

0 comments on commit bda5469

Please sign in to comment.