issue: move plugin depdending podvolume functions to util pkg

Signed-off-by: Lyndon-Li <lyonghui@vmware.com>
This commit is contained in:
Lyndon-Li
2023-09-26 16:39:33 +08:00
parent c3ec7b71c5
commit 2e71cffe0e
7 changed files with 466 additions and 412 deletions
-90
View File
@@ -37,14 +37,6 @@ const (
// TODO(2.0): remove
podAnnotationPrefix = "snapshot.velero.io/"
// VolumesToBackupAnnotation is the annotation on a pod whose mounted volumes
// need to be backed up using pod volume backup.
VolumesToBackupAnnotation = "backup.velero.io/backup-volumes"
// VolumesToExcludeAnnotation is the annotation on a pod whose mounted volumes
// should be excluded from pod volume backup.
VolumesToExcludeAnnotation = "backup.velero.io/backup-volumes-excludes"
// DefaultVolumesToFsBackup specifies whether pod volume backup should be used, by default, to
// take backup of all pod volumes.
DefaultVolumesToFsBackup = false
@@ -216,85 +208,3 @@ func getPodSnapshotAnnotations(obj metav1.Object) map[string]string {
return res
}
// GetVolumesToBackup returns a list of volume names to backup for
// the provided pod.
// Deprecated: Use GetVolumesByPod instead.
func GetVolumesToBackup(obj metav1.Object) []string {
annotations := obj.GetAnnotations()
if annotations == nil {
return nil
}
backupsValue := annotations[VolumesToBackupAnnotation]
if backupsValue == "" {
return nil
}
return strings.Split(backupsValue, ",")
}
func getVolumesToExclude(obj metav1.Object) []string {
annotations := obj.GetAnnotations()
if annotations == nil {
return nil
}
return strings.Split(annotations[VolumesToExcludeAnnotation], ",")
}
func contains(list []string, k string) bool {
for _, i := range list {
if i == k {
return true
}
}
return false
}
// GetVolumesByPod returns a list of volume names to backup for the provided pod.
func GetVolumesByPod(pod *corev1api.Pod, defaultVolumesToFsBackup bool) ([]string, []string) {
// tracks the volumes that have been explicitly opted out of backup via the annotation in the pod
optedOutVolumes := make([]string, 0)
if !defaultVolumesToFsBackup {
return GetVolumesToBackup(pod), optedOutVolumes
}
volsToExclude := getVolumesToExclude(pod)
podVolumes := []string{}
for _, pv := range pod.Spec.Volumes {
// cannot backup hostpath volumes as they are not mounted into /var/lib/kubelet/pods
// and therefore not accessible to the node agent daemon set.
if pv.HostPath != nil {
continue
}
// don't backup volumes mounting secrets. Secrets will be backed up separately.
if pv.Secret != nil {
continue
}
// don't backup volumes mounting config maps. Config maps will be backed up separately.
if pv.ConfigMap != nil {
continue
}
// don't backup volumes mounted as projected volumes, all data in those come from kube state.
if pv.Projected != nil {
continue
}
// don't backup DownwardAPI volumes, all data in those come from kube state.
if pv.DownwardAPI != nil {
continue
}
// don't backup volumes that are included in the exclude list.
if contains(volsToExclude, pv.Name) {
optedOutVolumes = append(optedOutVolumes, pv.Name)
continue
}
// don't include volumes that mount the default service account token.
if strings.HasPrefix(pv.Name, "default-token") {
continue
}
podVolumes = append(podVolumes, pv.Name)
}
return podVolumes, optedOutVolumes
}
-321
View File
@@ -17,12 +17,10 @@ limitations under the License.
package podvolume
import (
"sort"
"testing"
"github.com/stretchr/testify/assert"
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/builder"
@@ -303,322 +301,3 @@ func TestVolumeHasNonRestorableSource(t *testing.T) {
}
}
func TestGetVolumesToBackup(t *testing.T) {
tests := []struct {
name string
annotations map[string]string
expected []string
}{
{
name: "nil annotations",
annotations: nil,
expected: nil,
},
{
name: "no volumes to backup",
annotations: map[string]string{"foo": "bar"},
expected: nil,
},
{
name: "one volume to backup",
annotations: map[string]string{"foo": "bar", VolumesToBackupAnnotation: "volume-1"},
expected: []string{"volume-1"},
},
{
name: "multiple volumes to backup",
annotations: map[string]string{"foo": "bar", VolumesToBackupAnnotation: "volume-1,volume-2,volume-3"},
expected: []string{"volume-1", "volume-2", "volume-3"},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
pod := &corev1api.Pod{}
pod.Annotations = test.annotations
res := GetVolumesToBackup(pod)
// sort to ensure good compare of slices
sort.Strings(test.expected)
sort.Strings(res)
assert.Equal(t, test.expected, res)
})
}
}
func TestGetVolumesByPod(t *testing.T) {
testCases := []struct {
name string
pod *corev1api.Pod
expected struct {
included []string
optedOut []string
}
defaultVolumesToFsBackup bool
}{
{
name: "should get PVs from VolumesToBackupAnnotation when defaultVolumesToFsBackup is false",
defaultVolumesToFsBackup: false,
pod: &corev1api.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
VolumesToBackupAnnotation: "pvbPV1,pvbPV2,pvbPV3",
},
},
},
expected: struct {
included []string
optedOut []string
}{
included: []string{"pvbPV1", "pvbPV2", "pvbPV3"},
optedOut: []string{},
},
},
{
name: "should get all pod volumes when defaultVolumesToFsBackup is true and no PVs are excluded",
defaultVolumesToFsBackup: true,
pod: &corev1api.Pod{
Spec: corev1api.PodSpec{
Volumes: []corev1api.Volume{
// PVB Volumes
{Name: "pvbPV1"}, {Name: "pvbPV2"}, {Name: "pvbPV3"},
},
},
},
expected: struct {
included []string
optedOut []string
}{
included: []string{"pvbPV1", "pvbPV2", "pvbPV3"},
optedOut: []string{},
},
},
{
name: "should get all pod volumes except ones excluded when defaultVolumesToFsBackup is true",
defaultVolumesToFsBackup: true,
pod: &corev1api.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
VolumesToExcludeAnnotation: "nonPvbPV1,nonPvbPV2,nonPvbPV3",
},
},
Spec: corev1api.PodSpec{
Volumes: []corev1api.Volume{
// PVB Volumes
{Name: "pvbPV1"}, {Name: "pvbPV2"}, {Name: "pvbPV3"},
/// Excluded from PVB through annotation
{Name: "nonPvbPV1"}, {Name: "nonPvbPV2"}, {Name: "nonPvbPV3"},
},
},
},
expected: struct {
included []string
optedOut []string
}{
included: []string{"pvbPV1", "pvbPV2", "pvbPV3"},
optedOut: []string{"nonPvbPV1", "nonPvbPV2", "nonPvbPV3"},
},
},
{
name: "should exclude default service account token from pod volume backup",
defaultVolumesToFsBackup: true,
pod: &corev1api.Pod{
Spec: corev1api.PodSpec{
Volumes: []corev1api.Volume{
// PVB Volumes
{Name: "pvbPV1"}, {Name: "pvbPV2"}, {Name: "pvbPV3"},
/// Excluded from PVB because colume mounting default service account token
{Name: "default-token-5xq45"},
},
},
},
expected: struct {
included []string
optedOut []string
}{
included: []string{"pvbPV1", "pvbPV2", "pvbPV3"},
optedOut: []string{},
},
},
{
name: "should exclude host path volumes from pod volume backups",
defaultVolumesToFsBackup: true,
pod: &corev1api.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
VolumesToExcludeAnnotation: "nonPvbPV1,nonPvbPV2,nonPvbPV3",
},
},
Spec: corev1api.PodSpec{
Volumes: []corev1api.Volume{
// PVB Volumes
{Name: "pvbPV1"}, {Name: "pvbPV2"}, {Name: "pvbPV3"},
/// Excluded from pod volume backup through annotation
{Name: "nonPvbPV1"}, {Name: "nonPvbPV2"}, {Name: "nonPvbPV3"},
// Excluded from pod volume backup because hostpath
{Name: "hostPath1", VolumeSource: corev1api.VolumeSource{HostPath: &corev1api.HostPathVolumeSource{Path: "/hostpathVol"}}},
},
},
},
expected: struct {
included []string
optedOut []string
}{
included: []string{"pvbPV1", "pvbPV2", "pvbPV3"},
optedOut: []string{"nonPvbPV1", "nonPvbPV2", "nonPvbPV3"},
},
},
{
name: "should exclude volumes mounting secrets",
defaultVolumesToFsBackup: true,
pod: &corev1api.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
VolumesToExcludeAnnotation: "nonPvbPV1,nonPvbPV2,nonPvbPV3",
},
},
Spec: corev1api.PodSpec{
Volumes: []corev1api.Volume{
// PVB Volumes
{Name: "pvbPV1"}, {Name: "pvbPV2"}, {Name: "pvbPV3"},
/// Excluded from pod volume backup through annotation
{Name: "nonPvbPV1"}, {Name: "nonPvbPV2"}, {Name: "nonPvbPV3"},
// Excluded from pod volume backup because hostpath
{Name: "superSecret", VolumeSource: corev1api.VolumeSource{Secret: &corev1api.SecretVolumeSource{SecretName: "super-secret"}}},
},
},
},
expected: struct {
included []string
optedOut []string
}{
included: []string{"pvbPV1", "pvbPV2", "pvbPV3"},
optedOut: []string{"nonPvbPV1", "nonPvbPV2", "nonPvbPV3"},
},
},
{
name: "should exclude volumes mounting config maps",
defaultVolumesToFsBackup: true,
pod: &corev1api.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
VolumesToExcludeAnnotation: "nonPvbPV1,nonPvbPV2,nonPvbPV3",
},
},
Spec: corev1api.PodSpec{
Volumes: []corev1api.Volume{
// PVB Volumes
{Name: "pvbPV1"}, {Name: "pvbPV2"}, {Name: "pvbPV3"},
/// Excluded from pod volume backup through annotation
{Name: "nonPvbPV1"}, {Name: "nonPvbPV2"}, {Name: "nonPvbPV3"},
// Excluded from pod volume backup because hostpath
{Name: "appCOnfig", VolumeSource: corev1api.VolumeSource{ConfigMap: &corev1api.ConfigMapVolumeSource{LocalObjectReference: corev1api.LocalObjectReference{Name: "app-config"}}}},
},
},
},
expected: struct {
included []string
optedOut []string
}{
included: []string{"pvbPV1", "pvbPV2", "pvbPV3"},
optedOut: []string{"nonPvbPV1", "nonPvbPV2", "nonPvbPV3"},
},
},
{
name: "should exclude projected volumes",
defaultVolumesToFsBackup: true,
pod: &corev1api.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
VolumesToExcludeAnnotation: "nonPvbPV1,nonPvbPV2,nonPvbPV3",
},
},
Spec: corev1api.PodSpec{
Volumes: []corev1api.Volume{
{Name: "pvbPV1"}, {Name: "pvbPV2"}, {Name: "pvbPV3"},
{
Name: "projected",
VolumeSource: corev1api.VolumeSource{
Projected: &corev1api.ProjectedVolumeSource{
Sources: []corev1api.VolumeProjection{{
Secret: &corev1api.SecretProjection{
LocalObjectReference: corev1api.LocalObjectReference{},
Items: nil,
Optional: nil,
},
DownwardAPI: nil,
ConfigMap: nil,
ServiceAccountToken: nil,
}},
DefaultMode: nil,
},
},
},
},
},
},
expected: struct {
included []string
optedOut []string
}{
included: []string{"pvbPV1", "pvbPV2", "pvbPV3"},
optedOut: []string{},
},
},
{
name: "should exclude DownwardAPI volumes",
defaultVolumesToFsBackup: true,
pod: &corev1api.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
VolumesToExcludeAnnotation: "nonPvbPV1,nonPvbPV2,nonPvbPV3",
},
},
Spec: corev1api.PodSpec{
Volumes: []corev1api.Volume{
{Name: "pvbPV1"}, {Name: "pvbPV2"}, {Name: "pvbPV3"},
{
Name: "downwardAPI",
VolumeSource: corev1api.VolumeSource{
DownwardAPI: &corev1api.DownwardAPIVolumeSource{
Items: []corev1api.DownwardAPIVolumeFile{
{
Path: "labels",
FieldRef: &corev1api.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.labels",
},
},
},
},
},
},
},
},
},
expected: struct {
included []string
optedOut []string
}{
included: []string{"pvbPV1", "pvbPV2", "pvbPV3"},
optedOut: []string{},
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
actualIncluded, actualOptedOut := GetVolumesByPod(tc.pod, tc.defaultVolumesToFsBackup)
sort.Strings(tc.expected.included)
sort.Strings(actualIncluded)
assert.Equal(t, tc.expected.included, actualIncluded)
sort.Strings(tc.expected.optedOut)
sort.Strings(actualOptedOut)
assert.Equal(t, tc.expected.optedOut, actualOptedOut)
})
}
}