mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-09-28 10:45:44 +00:00
276 lines
11 KiB
Go
276 lines
11 KiB
Go
/*
|
|
Copyright 2017 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 actions
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
corev1api "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
|
"github.com/vmware-tanzu/velero/pkg/builder"
|
|
factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks"
|
|
"github.com/vmware-tanzu/velero/pkg/kuberesource"
|
|
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
|
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
|
)
|
|
|
|
func TestBackupPVAction(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
pvc *corev1api.PersistentVolumeClaim
|
|
pods []*corev1api.Pod
|
|
expectedErr error
|
|
expectedRelated []velero.ResourceIdentifier
|
|
}{
|
|
{
|
|
name: "Test no volumeName",
|
|
pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").Phase(corev1api.ClaimBound).Result(),
|
|
expectedErr: nil,
|
|
expectedRelated: nil,
|
|
},
|
|
{
|
|
name: "Test empty volumeName",
|
|
pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("").Phase(corev1api.ClaimBound).Result(),
|
|
expectedErr: nil,
|
|
expectedRelated: nil,
|
|
},
|
|
{
|
|
name: "Test no status phase",
|
|
pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").Result(),
|
|
expectedErr: nil,
|
|
expectedRelated: nil,
|
|
},
|
|
{
|
|
name: "Test pending status phase",
|
|
pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").Phase(corev1api.ClaimPending).Result(),
|
|
expectedErr: nil,
|
|
expectedRelated: nil,
|
|
},
|
|
{
|
|
name: "Test lost status phase",
|
|
pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").Phase(corev1api.ClaimLost).Result(),
|
|
expectedErr: nil,
|
|
expectedRelated: nil,
|
|
},
|
|
{
|
|
name: "Test with volume",
|
|
pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").Phase(corev1api.ClaimBound).Result(),
|
|
expectedErr: nil,
|
|
expectedRelated: []velero.ResourceIdentifier{
|
|
{GroupResource: kuberesource.PersistentVolumes, Name: "testPV"},
|
|
},
|
|
},
|
|
{
|
|
name: "Test with volume and one running pod",
|
|
pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").Phase(corev1api.ClaimBound).Result(),
|
|
pods: []*corev1api.Pod{
|
|
builder.ForPod("velero", "testPod1").Volumes(builder.ForVolume("testPVC").PersistentVolumeClaimSource("testPVC").Result()).NodeName("velero").Phase(corev1api.PodRunning).Result(),
|
|
},
|
|
expectedErr: nil,
|
|
expectedRelated: []velero.ResourceIdentifier{
|
|
{GroupResource: kuberesource.PersistentVolumes, Name: "testPV"},
|
|
{GroupResource: kuberesource.Pods, Namespace: "velero", Name: "testPod1"},
|
|
},
|
|
},
|
|
{
|
|
name: "Test with volume and multiple running pods",
|
|
pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").Phase(corev1api.ClaimBound).Result(),
|
|
pods: []*corev1api.Pod{
|
|
builder.ForPod("velero", "testPod1").Volumes(builder.ForVolume("testPVC").PersistentVolumeClaimSource("testPVC").Result()).NodeName("velero").Phase(corev1api.PodRunning).Result(),
|
|
builder.ForPod("velero", "testPod2").Volumes(builder.ForVolume("testPVC").PersistentVolumeClaimSource("testPVC").Result()).NodeName("velero").Phase(corev1api.PodRunning).Result(),
|
|
builder.ForPod("velero", "testPod3").Volumes(builder.ForVolume("testPVC").PersistentVolumeClaimSource("testPVC").Result()).NodeName("velero").Phase(corev1api.PodRunning).Result(),
|
|
},
|
|
expectedErr: nil,
|
|
expectedRelated: []velero.ResourceIdentifier{
|
|
{GroupResource: kuberesource.PersistentVolumes, Name: "testPV"},
|
|
{GroupResource: kuberesource.Pods, Namespace: "velero", Name: "testPod1"},
|
|
{GroupResource: kuberesource.Pods, Namespace: "velero", Name: "testPod2"},
|
|
{GroupResource: kuberesource.Pods, Namespace: "velero", Name: "testPod3"},
|
|
},
|
|
},
|
|
{
|
|
name: "Test with volume and multiple running pods, some not running",
|
|
pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").Phase(corev1api.ClaimBound).Result(),
|
|
pods: []*corev1api.Pod{
|
|
builder.ForPod("velero", "testPod1").Volumes(builder.ForVolume("testPVC").PersistentVolumeClaimSource("testPVC").Result()).NodeName("velero").Phase(corev1api.PodSucceeded).Result(),
|
|
builder.ForPod("velero", "testPod2").Volumes(builder.ForVolume("testPVC").PersistentVolumeClaimSource("testPVC").Result()).NodeName("velero").Phase(corev1api.PodRunning).Result(),
|
|
builder.ForPod("velero", "testPod3").Volumes(builder.ForVolume("testPVC").PersistentVolumeClaimSource("testPVC").Result()).Phase(corev1api.PodRunning).Result(),
|
|
},
|
|
expectedErr: nil,
|
|
expectedRelated: []velero.ResourceIdentifier{
|
|
{GroupResource: kuberesource.PersistentVolumes, Name: "testPV"},
|
|
{GroupResource: kuberesource.Pods, Namespace: "velero", Name: "testPod2"},
|
|
},
|
|
},
|
|
{
|
|
name: "Test with PVC grouping via VGS label",
|
|
pvc: builder.ForPersistentVolumeClaim("velero", "testPVC-1").ObjectMeta(builder.WithLabels("velero.io/group", "db")).VolumeName("testPV-1").Phase(corev1api.ClaimBound).Result(),
|
|
pods: []*corev1api.Pod{
|
|
builder.ForPod("velero", "testPod-1").
|
|
Volumes(builder.ForVolume("testPV-1").PersistentVolumeClaimSource("testPVC-1").Result()).
|
|
NodeName("node").
|
|
Phase(corev1api.PodRunning).Result(),
|
|
},
|
|
expectedErr: nil,
|
|
expectedRelated: []velero.ResourceIdentifier{
|
|
{GroupResource: kuberesource.PersistentVolumes, Name: "testPV-1"},
|
|
{GroupResource: kuberesource.Pods, Namespace: "velero", Name: "testPod-1"},
|
|
{GroupResource: kuberesource.PersistentVolumeClaims, Namespace: "velero", Name: "groupedPVC"},
|
|
},
|
|
},
|
|
}
|
|
|
|
backup := &v1.Backup{}
|
|
logger := logrus.New()
|
|
|
|
f := &factorymocks.Factory{}
|
|
f.On("KubebuilderClient").Return(nil, fmt.Errorf(""))
|
|
plugin := NewPVCAction(f)
|
|
_, err := plugin(logger)
|
|
require.Error(t, err)
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(*testing.T) {
|
|
crClient := velerotest.NewFakeControllerRuntimeClient(t)
|
|
f := &factorymocks.Factory{}
|
|
f.On("KubebuilderClient").Return(crClient, nil)
|
|
plugin := NewPVCAction(f)
|
|
i, err := plugin(logger)
|
|
require.NoError(t, err)
|
|
a := i.(*PVCAction)
|
|
|
|
if tc.pvc != nil {
|
|
require.NoError(t, crClient.Create(t.Context(), tc.pvc))
|
|
}
|
|
for _, pod := range tc.pods {
|
|
require.NoError(t, crClient.Create(t.Context(), pod))
|
|
}
|
|
|
|
if tc.name == "Test with PVC grouping via VGS label" {
|
|
groupedPVC := builder.ForPersistentVolumeClaim("velero", "groupedPVC").ObjectMeta(builder.WithLabels("velero.io/group", "db")).VolumeName("groupedPV").Phase(corev1api.ClaimBound).Result()
|
|
require.NoError(t, crClient.Create(t.Context(), groupedPVC))
|
|
backup.Spec.VolumeGroupSnapshotLabelKey = "velero.io/group"
|
|
}
|
|
|
|
pvcMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&tc.pvc)
|
|
require.NoError(t, err)
|
|
|
|
relatedItems, err := a.GetRelatedItems(&unstructured.Unstructured{Object: pvcMap}, backup)
|
|
if tc.expectedErr != nil {
|
|
require.EqualError(t, err, tc.expectedErr.Error())
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
assert.Equal(t, tc.expectedRelated, relatedItems)
|
|
})
|
|
}
|
|
}
|
|
|
|
// Test_getGroupedPVCs verifies the PVC grouping logic for VolumeGroupSnapshots.
|
|
// This ensures only same-namespace PVCs with the same label key and value are included.
|
|
func Test_getGroupedPVCs(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
labelKey string
|
|
groupValue string
|
|
existingPVCs []*corev1api.PersistentVolumeClaim
|
|
targetPVC *corev1api.PersistentVolumeClaim
|
|
expectedRelated []velero.ResourceIdentifier
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "No label key in spec",
|
|
labelKey: "",
|
|
targetPVC: builder.ForPersistentVolumeClaim("ns", "pvc-1").Result(),
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "No group value",
|
|
labelKey: "velero.io/group",
|
|
groupValue: "",
|
|
targetPVC: builder.ForPersistentVolumeClaim("ns", "pvc-1").Result(),
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Target PVC does not have the label",
|
|
labelKey: "velero.io/group",
|
|
targetPVC: builder.ForPersistentVolumeClaim("ns", "pvc-1").Result(),
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Target PVC has label, but no group matches",
|
|
labelKey: "velero.io/group",
|
|
groupValue: "group-1",
|
|
targetPVC: builder.ForPersistentVolumeClaim("ns", "pvc-1").ObjectMeta(builder.WithLabels("velero.io/group", "group-1")).Result(),
|
|
existingPVCs: []*corev1api.PersistentVolumeClaim{
|
|
builder.ForPersistentVolumeClaim("ns", "pvc-1").ObjectMeta(builder.WithLabels("velero.io/group", "group-1")).Result(),
|
|
},
|
|
expectError: false,
|
|
expectedRelated: nil,
|
|
},
|
|
{
|
|
name: "Multiple PVCs in the same group",
|
|
labelKey: "velero.io/group",
|
|
groupValue: "group-1",
|
|
targetPVC: builder.ForPersistentVolumeClaim("ns", "pvc-1").ObjectMeta(builder.WithLabels("velero.io/group", "group-1")).Result(),
|
|
existingPVCs: []*corev1api.PersistentVolumeClaim{
|
|
builder.ForPersistentVolumeClaim("ns", "pvc-1").ObjectMeta(builder.WithLabels("velero.io/group", "group-1")).Result(),
|
|
builder.ForPersistentVolumeClaim("ns", "pvc-2").ObjectMeta(builder.WithLabels("velero.io/group", "group-1")).Result(),
|
|
builder.ForPersistentVolumeClaim("ns", "pvc-3").ObjectMeta(builder.WithLabels("velero.io/group", "group-1")).Result(),
|
|
},
|
|
expectError: false,
|
|
expectedRelated: []velero.ResourceIdentifier{
|
|
{GroupResource: kuberesource.PersistentVolumeClaims, Namespace: "ns", Name: "pvc-2"},
|
|
{GroupResource: kuberesource.PersistentVolumeClaims, Namespace: "ns", Name: "pvc-3"},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
crClient := velerotest.NewFakeControllerRuntimeClient(t)
|
|
for _, pvc := range tc.existingPVCs {
|
|
require.NoError(t, crClient.Create(t.Context(), pvc))
|
|
}
|
|
|
|
logger := logrus.New()
|
|
a := &PVCAction{
|
|
log: logger,
|
|
crClient: crClient,
|
|
}
|
|
|
|
backup := builder.ForBackup("ns", "bkp").VolumeGroupSnapshotLabelKey(tc.labelKey).Result()
|
|
|
|
related, err := a.getGroupedPVCs(t.Context(), tc.targetPVC, backup)
|
|
if tc.expectError {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
assert.ElementsMatch(t, tc.expectedRelated, related)
|
|
})
|
|
}
|
|
}
|