Merge pull request #9567 from hollycai05/add-e2e-tests-for-issue7725

Add e2e test case for issue 7725
This commit is contained in:
Daniel Jiang
2026-04-28 14:01:18 +08:00
committed by GitHub
2 changed files with 259 additions and 0 deletions

View File

@@ -685,6 +685,12 @@ var _ = Describe(
LoadAffinities,
)
var _ = Describe(
"Test data mover dynamic Cache PVC injection",
Label("NodeAgentConfig", "CachePVC"),
CachePVCTest,
)
func GetKubeConfigContext() error {
var err error
var tcDefault, tcStandby k8s.TestClient

View File

@@ -0,0 +1,253 @@
/*
Copyright the Velero contributors.
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.
*/
package nodeagentconfig
import (
"context"
"fmt"
"strings"
"time"
. "github.com/onsi/gomega"
"github.com/pkg/errors"
corev1api "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1"
"github.com/vmware-tanzu/velero/pkg/builder"
"github.com/vmware-tanzu/velero/test"
. "github.com/vmware-tanzu/velero/test/e2e/test"
k8sutil "github.com/vmware-tanzu/velero/test/util/k8s"
veleroutil "github.com/vmware-tanzu/velero/test/util/velero"
)
type CachePVCTestCase struct {
TestCase
nodeAgentConfigMapName string
repoConfigMapName string
}
var CachePVCTest func() = TestFunc(&CachePVCTestCase{
nodeAgentConfigMapName: "node-agent-config-cache",
repoConfigMapName: "backup-repo-config-cache",
})
func (c *CachePVCTestCase) Init() error {
c.TestCase.Init()
c.CaseBaseName = "cache-pvc-" + c.UUIDgen
c.BackupName = "backup-" + c.CaseBaseName
c.RestoreName = "restore-" + c.CaseBaseName
c.NamespacesTotal = 1
c.NSIncluded = &[]string{}
for nsNum := 0; nsNum < c.NamespacesTotal; nsNum++ {
createNSName := fmt.Sprintf("%s-%00000d", c.CaseBaseName, nsNum)
*c.NSIncluded = append(*c.NSIncluded, createNSName)
}
c.VeleroCfg.UseNodeAgent = true
c.VeleroCfg.UseNodeAgentWindows = true
// Ensure Data Mover is used to trigger DataUpload/DataDownload pods
c.BackupArgs = []string{
"create", "--namespace", c.VeleroCfg.VeleroNamespace, "backup", c.BackupName,
"--include-namespaces", strings.Join(*c.NSIncluded, ","),
"--snapshot-volumes=true", "--snapshot-move-data",
}
c.RestoreArgs = []string{
"create", "--namespace", c.VeleroCfg.VeleroNamespace, "restore", c.RestoreName,
"--from-backup", c.BackupName,
}
c.TestMsg = &TestMSG{
Desc: "Validate dynamically provisioned Cache PVC for data mover pods",
FailedMSG: "Failed to apply and validate cache PVC configuration in data mover pods.",
Text: "Should dynamically provision a Cache PVC for data mover restore pods to avoid node root FS exhaustion.",
}
return nil
}
func (c *CachePVCTestCase) InstallVelero() error {
fmt.Println("Start to uninstall Velero")
if err := veleroutil.VeleroUninstall(c.Ctx, c.VeleroCfg); err != nil {
fmt.Printf("Fail to uninstall Velero: %s\n", err.Error())
return err
}
// 1. Construct node-agent ConfigMap (Define Cache PVC StorageClass and trigger threshold)
// Set residentThresholdInMB to 0 to force Velero to create a PVC even for tiny E2E test data.
nodeAgentConfigJSON := fmt.Sprintf(`{"cachePVC": {"residentThresholdInMB": 0, "storageClass": "%s"}}`, test.StorageClassName)
nodeAgentConfig := builder.ForConfigMap(c.VeleroCfg.VeleroNamespace, c.nodeAgentConfigMapName).
Data("node-agent-config", nodeAgentConfigJSON).Result()
// 2. Construct backup repository ConfigMap (Define cacheLimitMB)
repoConfigJSON := `{"cacheLimitMB": 2048}` // Set 2GB cache limit
repoConfig := builder.ForConfigMap(c.VeleroCfg.VeleroNamespace, c.repoConfigMapName).
Data(test.UploaderTypeKopia, repoConfigJSON).Result()
c.VeleroCfg.NodeAgentConfigMap = c.nodeAgentConfigMapName
c.VeleroCfg.BackupRepoConfigMap = c.repoConfigMapName
// Deploy Velero with the two Cache configuration ConfigMaps
return veleroutil.PrepareVelero(c.Ctx, c.CaseBaseName, c.VeleroCfg, nodeAgentConfig, repoConfig)
}
func (c *CachePVCTestCase) CreateResources() error {
for _, ns := range *c.NSIncluded {
if err := k8sutil.CreateNamespace(c.Ctx, c.Client, ns); err != nil {
return err
}
pvc, err := k8sutil.CreatePVC(c.Client, ns, "volume-1", test.StorageClassName, nil)
if err != nil {
return err
}
vols := k8sutil.CreateVolumes(pvc.Name, []string{"volume-1"})
deployment := k8sutil.NewDeployment(
c.CaseBaseName,
ns,
1,
map[string]string{"app": "test"},
c.VeleroCfg.ImageRegistryProxy,
c.VeleroCfg.WorkerOS,
).WithVolume(vols).Result()
deployment, err = k8sutil.CreateDeployment(c.Client.ClientGo, ns, deployment)
if err != nil {
return errors.Wrap(err, "failed to create deployment")
}
if err := k8sutil.WaitForReadyDeployment(c.Client.ClientGo, deployment.Namespace, deployment.Name); err != nil {
return err
}
}
return nil
}
// verifyCacheVolumeInPod correctly verifies that the Pod mounted a dynamic PVC instead of an emptyDir
func (c *CachePVCTestCase) verifyCacheVolumeInPod(pod corev1api.Pod) error {
for _, vol := range pod.Spec.Volumes {
if vol.PersistentVolumeClaim != nil {
// Velero dynamically provisioned cache volumes typically have 'cache' in the name
if strings.Contains(vol.PersistentVolumeClaim.ClaimName, "cache") || strings.Contains(vol.Name, "cache") {
return nil // Success: Found the dynamically provisioned Cache PVC
}
}
}
return fmt.Errorf("Dynamically provisioned Cache PVC not found in pod %s, feature failed!", pod.Name)
}
func (c *CachePVCTestCase) Backup() error {
if err := veleroutil.VeleroCmdExec(c.Ctx, c.VeleroCfg.VeleroCLI, c.BackupArgs); err != nil {
return err
}
fmt.Println("Waiting for backup to complete...")
// Wait for backup completion
wait.PollUntilContextTimeout(c.Ctx, 5*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) {
backup := new(velerov1api.Backup)
if err := c.VeleroCfg.ClientToInstallVelero.Kubebuilder.Get(
c.Ctx,
client.ObjectKey{Namespace: c.VeleroCfg.VeleroNamespace, Name: c.BackupName},
backup,
); err != nil {
return false, err
}
if backup.Status.Phase != velerov1api.BackupPhaseCompleted &&
backup.Status.Phase != velerov1api.BackupPhaseFailed &&
backup.Status.Phase != velerov1api.BackupPhasePartiallyFailed {
return false, nil
}
return true, nil
})
return nil
}
func (c *CachePVCTestCase) Restore() error {
if err := veleroutil.VeleroCmdExec(c.Ctx, c.VeleroCfg.VeleroCLI, c.RestoreArgs); err != nil {
return err
}
restorePodList := new(corev1api.PodList)
wait.PollUntilContextTimeout(c.Ctx, 5*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) {
ddList := new(velerov2alpha1api.DataDownloadList)
if err := c.VeleroCfg.ClientToInstallVelero.Kubebuilder.List(
c.Ctx,
ddList,
&client.ListOptions{Namespace: c.VeleroCfg.VeleroNamespace},
); err != nil {
return false, err
} else if len(ddList.Items) <= 0 {
return false, nil
}
if err := c.VeleroCfg.ClientToInstallVelero.Kubebuilder.List(
c.Ctx,
restorePodList,
&client.ListOptions{
LabelSelector: labels.SelectorFromSet(map[string]string{
velerov1api.DataDownloadLabel: ddList.Items[0].Name,
}),
}); err != nil {
return false, err
} else if len(restorePodList.Items) <= 0 {
return false, nil
}
return true, nil
})
fmt.Println("Start to verify restore data mover pod content.")
Expect(restorePodList.Items).ToNot(BeEmpty())
// Ensure the Data Mover pod is using a true PVC for caching
err := c.verifyCacheVolumeInPod(restorePodList.Items[0])
Expect(err).To(Succeed(), "Injected Cache PVC should exist in DataDownload Pod")
fmt.Println("Restore data mover pod content verification completed successfully.")
// Wait for restore completion
wait.PollUntilContextTimeout(c.Ctx, 5*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) {
restore := new(velerov1api.Restore)
if err := c.VeleroCfg.ClientToInstallVelero.Kubebuilder.Get(
c.Ctx,
client.ObjectKey{Namespace: c.VeleroCfg.VeleroNamespace, Name: c.RestoreName},
restore,
); err != nil {
return false, err
}
if restore.Status.Phase != velerov1api.RestorePhaseCompleted &&
restore.Status.Phase != velerov1api.RestorePhaseFailed &&
restore.Status.Phase != velerov1api.RestorePhasePartiallyFailed {
return false, nil
}
return true, nil
})
return nil
}