mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-08-15 11:46:06 +00:00
Validate restore name label length
Velero should handle cases when the label length exceeds 63 characters. - if the length of the backup/restore name is <= 63 characters, use it as the value of the label - if it's > 63 characters, take the SHA256 hash of the name. the value of the label will be the first 57 characters of the backup/restore name plus the first six characters of the SHA256 hash. Fixes heptio#1021 Signed-off-by: Anshul Chandra <anshulc@vmware.com>
This commit is contained in:
@@ -28,11 +28,13 @@ import (
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
core "k8s.io/client-go/testing"
|
||||
|
||||
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
"github.com/heptio/velero/pkg/generated/clientset/versioned/fake"
|
||||
informers "github.com/heptio/velero/pkg/generated/informers/externalversions"
|
||||
"github.com/heptio/velero/pkg/label"
|
||||
"github.com/heptio/velero/pkg/persistence"
|
||||
persistencemocks "github.com/heptio/velero/pkg/persistence/mocks"
|
||||
"github.com/heptio/velero/pkg/plugin/clientmgmt"
|
||||
@@ -73,13 +75,47 @@ func defaultLocationsList(namespace string) []*velerov1api.BackupStorageLocation
|
||||
}
|
||||
}
|
||||
|
||||
func defaultLocationsListWithLongerLocationName(namespace string) []*velerov1api.BackupStorageLocation {
|
||||
return []*velerov1api.BackupStorageLocation{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: "the-really-long-location-name-that-is-much-more-than-63-characters-1",
|
||||
},
|
||||
Spec: velerov1api.BackupStorageLocationSpec{
|
||||
Provider: "objStoreProvider",
|
||||
StorageType: velerov1api.StorageType{
|
||||
ObjectStorage: &velerov1api.ObjectStorageLocation{
|
||||
Bucket: "bucket-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: "the-really-long-location-name-that-is-much-more-than-63-characters-2",
|
||||
},
|
||||
Spec: velerov1api.BackupStorageLocationSpec{
|
||||
Provider: "objStoreProvider",
|
||||
StorageType: velerov1api.StorageType{
|
||||
ObjectStorage: &velerov1api.ObjectStorageLocation{
|
||||
Bucket: "bucket-2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackupSyncControllerRun(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
namespace string
|
||||
locations []*velerov1api.BackupStorageLocation
|
||||
cloudBackups map[string][]*velerov1api.Backup
|
||||
existingBackups []*velerov1api.Backup
|
||||
name string
|
||||
namespace string
|
||||
locations []*velerov1api.BackupStorageLocation
|
||||
cloudBackups map[string][]*velerov1api.Backup
|
||||
existingBackups []*velerov1api.Backup
|
||||
longLocationNameEnabled bool
|
||||
}{
|
||||
{
|
||||
name: "no cloud backups",
|
||||
@@ -163,6 +199,21 @@ func TestBackupSyncControllerRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "backup storage location names and labels get updated with location name greater than 63 chars",
|
||||
namespace: "ns-1",
|
||||
locations: defaultLocationsListWithLongerLocationName("ns-1"),
|
||||
longLocationNameEnabled: true,
|
||||
cloudBackups: map[string][]*velerov1api.Backup{
|
||||
"bucket-1": {
|
||||
velerotest.NewTestBackup().WithNamespace("ns-1").WithName("backup-1").WithStorageLocation("foo").WithLabel(velerov1api.StorageLocationLabel, "foo").Backup,
|
||||
velerotest.NewTestBackup().WithNamespace("ns-1").WithName("backup-2").Backup,
|
||||
},
|
||||
"bucket-2": {
|
||||
velerotest.NewTestBackup().WithNamespace("ns-1").WithName("backup-3").WithStorageLocation("bar").WithLabel(velerov1api.StorageLocationLabel, "bar").Backup,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -259,7 +310,13 @@ func TestBackupSyncControllerRun(t *testing.T) {
|
||||
} else {
|
||||
// verify that the storage location field and label are set properly
|
||||
assert.Equal(t, location.Name, obj.Spec.StorageLocation)
|
||||
assert.Equal(t, location.Name, obj.Labels[velerov1api.StorageLocationLabel])
|
||||
|
||||
locationName := location.Name
|
||||
if test.longLocationNameEnabled {
|
||||
locationName = label.GetValidName(locationName)
|
||||
}
|
||||
assert.Equal(t, locationName, obj.Labels[velerov1api.StorageLocationLabel])
|
||||
assert.Equal(t, true, len(obj.Labels[velerov1api.StorageLocationLabel]) <= validation.DNS1035LabelMaxLength)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -402,6 +459,84 @@ func TestDeleteOrphanedBackups(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageLabelsInDeleteOrphanedBackups(t *testing.T) {
|
||||
longLabelName := "the-really-long-location-name-that-is-much-more-than-63-characters"
|
||||
tests := []struct {
|
||||
name string
|
||||
cloudBackups sets.String
|
||||
k8sBackups []*velerotest.TestBackup
|
||||
namespace string
|
||||
expectedDeletes sets.String
|
||||
}{
|
||||
{
|
||||
name: "some overlapping backups",
|
||||
namespace: "ns-1",
|
||||
cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"),
|
||||
k8sBackups: []*velerotest.TestBackup{
|
||||
velerotest.NewTestBackup().WithNamespace("ns-1").WithName("backup-1").
|
||||
WithLabel(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779").WithPhase(velerov1api.BackupPhaseCompleted),
|
||||
velerotest.NewTestBackup().WithNamespace("ns-1").WithName("backup-2").
|
||||
WithLabel(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779").WithPhase(velerov1api.BackupPhaseCompleted),
|
||||
velerotest.NewTestBackup().WithNamespace("ns-1").WithName("backup-C").
|
||||
WithLabel(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779").WithPhase(velerov1api.BackupPhaseCompleted),
|
||||
},
|
||||
expectedDeletes: sets.NewString("backup-C"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var (
|
||||
client = fake.NewSimpleClientset()
|
||||
sharedInformers = informers.NewSharedInformerFactory(client, 0)
|
||||
)
|
||||
|
||||
c := NewBackupSyncController(
|
||||
client.VeleroV1(),
|
||||
client.VeleroV1(),
|
||||
sharedInformers.Velero().V1().Backups(),
|
||||
sharedInformers.Velero().V1().BackupStorageLocations(),
|
||||
time.Duration(0),
|
||||
test.namespace,
|
||||
"",
|
||||
nil, // new plugin manager func
|
||||
velerotest.NewLogger(),
|
||||
).(*backupSyncController)
|
||||
|
||||
expectedDeleteActions := make([]core.Action, 0)
|
||||
|
||||
for _, backup := range test.k8sBackups {
|
||||
// add test backup to informer
|
||||
require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(backup.Backup), "Error adding backup to informer")
|
||||
|
||||
// add test backup to client
|
||||
_, err := client.VeleroV1().Backups(test.namespace).Create(backup.Backup)
|
||||
require.NoError(t, err, "Error adding backup to clientset")
|
||||
|
||||
// if we expect this backup to be deleted, set up the expected DeleteAction
|
||||
if test.expectedDeletes.Has(backup.Name) {
|
||||
actionDelete := core.NewDeleteAction(
|
||||
velerov1api.SchemeGroupVersion.WithResource("backups"),
|
||||
test.namespace,
|
||||
backup.Name,
|
||||
)
|
||||
expectedDeleteActions = append(expectedDeleteActions, actionDelete)
|
||||
}
|
||||
}
|
||||
|
||||
c.deleteOrphanedBackups(longLabelName, test.cloudBackups, velerotest.NewLogger())
|
||||
|
||||
numBackups, err := numBackups(t, client, c.namespace)
|
||||
assert.NoError(t, err)
|
||||
|
||||
expected := len(test.k8sBackups) - len(test.expectedDeletes)
|
||||
assert.Equal(t, expected, numBackups)
|
||||
|
||||
velerotest.CompareActions(t, expectedDeleteActions, getDeleteActions(client.Actions()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldSync(t *testing.T) {
|
||||
c := clock.NewFakeClock(time.Now())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user