2017-08-02 17:27:17 +00:00
/ *
2019-05-29 18:21:25 +00:00
Copyright 2017 , 2019 the Velero contributors .
2017-08-02 17:27:17 +00:00
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 controller
import (
2018-07-10 22:17:53 +00:00
"bytes"
2018-09-26 22:18:45 +00:00
"fmt"
2017-08-02 17:27:17 +00:00
"io"
2018-09-26 22:18:45 +00:00
"sort"
2018-07-10 22:17:53 +00:00
"strings"
2017-08-02 17:27:17 +00:00
"testing"
"time"
2018-09-25 14:51:28 +00:00
"github.com/pkg/errors"
2018-08-20 23:29:54 +00:00
"github.com/sirupsen/logrus"
2018-09-26 22:18:45 +00:00
"github.com/stretchr/testify/assert"
2018-08-20 23:29:54 +00:00
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
2018-06-20 18:08:07 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2017-08-02 17:27:17 +00:00
"k8s.io/apimachinery/pkg/util/clock"
2019-09-30 21:26:56 +00:00
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
pkgbackup "github.com/vmware-tanzu/velero/pkg/backup"
"github.com/vmware-tanzu/velero/pkg/builder"
"github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake"
informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions"
"github.com/vmware-tanzu/velero/pkg/metrics"
"github.com/vmware-tanzu/velero/pkg/persistence"
persistencemocks "github.com/vmware-tanzu/velero/pkg/persistence/mocks"
"github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt"
pluginmocks "github.com/vmware-tanzu/velero/pkg/plugin/mocks"
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
"github.com/vmware-tanzu/velero/pkg/util/logging"
2017-08-02 17:27:17 +00:00
)
type fakeBackupper struct {
mock . Mock
}
2019-03-27 18:22:04 +00:00
func ( b * fakeBackupper ) Backup ( logger logrus . FieldLogger , backup * pkgbackup . Request , backupFile io . Writer , actions [ ] velero . BackupItemAction , volumeSnapshotterGetter pkgbackup . VolumeSnapshotterGetter ) error {
args := b . Called ( logger , backup , backupFile , actions , volumeSnapshotterGetter )
2017-08-02 17:27:17 +00:00
return args . Error ( 0 )
}
2019-07-31 14:46:48 +00:00
func defaultBackup ( ) * builder . BackupBuilder {
return builder . ForBackup ( velerov1api . DefaultNamespace , "backup-1" )
2019-06-21 21:08:08 +00:00
}
2019-04-24 17:54:43 +00:00
func TestProcessBackupNonProcessedItems ( t * testing . T ) {
2017-08-02 17:27:17 +00:00
tests := [ ] struct {
2019-04-24 17:54:43 +00:00
name string
key string
2019-06-21 21:08:08 +00:00
backup * velerov1api . Backup
2017-08-02 17:27:17 +00:00
} {
{
2019-04-23 23:19:00 +00:00
name : "bad key does not return error" ,
key : "bad/key/here" ,
2017-08-02 17:27:17 +00:00
} ,
{
2019-04-23 23:19:00 +00:00
name : "backup not found in lister does not return error" ,
key : "nonexistent/backup" ,
2017-08-02 17:27:17 +00:00
} ,
{
2018-09-26 22:18:45 +00:00
name : "FailedValidation backup is not processed" ,
2019-01-25 03:33:07 +00:00
key : "velero/backup-1" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . Phase ( velerov1api . BackupPhaseFailedValidation ) . Result ( ) ,
2017-08-02 17:27:17 +00:00
} ,
{
2018-09-26 22:18:45 +00:00
name : "InProgress backup is not processed" ,
2019-01-25 03:33:07 +00:00
key : "velero/backup-1" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . Phase ( velerov1api . BackupPhaseInProgress ) . Result ( ) ,
2017-08-02 17:27:17 +00:00
} ,
{
2018-09-26 22:18:45 +00:00
name : "Completed backup is not processed" ,
2019-01-25 03:33:07 +00:00
key : "velero/backup-1" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . Phase ( velerov1api . BackupPhaseCompleted ) . Result ( ) ,
2017-08-02 17:27:17 +00:00
} ,
{
2018-09-26 22:18:45 +00:00
name : "Failed backup is not processed" ,
2019-01-25 03:33:07 +00:00
key : "velero/backup-1" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . Phase ( velerov1api . BackupPhaseFailed ) . Result ( ) ,
2017-08-09 22:52:27 +00:00
} ,
2018-09-26 22:18:45 +00:00
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
2019-07-30 23:29:34 +00:00
formatFlag := logging . FormatText
2018-09-26 22:18:45 +00:00
var (
sharedInformers = informers . NewSharedInformerFactory ( fake . NewSimpleClientset ( ) , 0 )
2019-07-30 23:29:34 +00:00
logger = logging . DefaultLogger ( logrus . DebugLevel , formatFlag )
2018-09-26 22:18:45 +00:00
)
c := & backupController {
genericController : newGenericController ( "backup-test" , logger ) ,
2019-01-25 03:33:07 +00:00
lister : sharedInformers . Velero ( ) . V1 ( ) . Backups ( ) . Lister ( ) ,
2019-07-30 23:29:34 +00:00
formatFlag : formatFlag ,
2018-09-26 22:18:45 +00:00
}
if test . backup != nil {
2019-01-25 03:33:07 +00:00
require . NoError ( t , sharedInformers . Velero ( ) . V1 ( ) . Backups ( ) . Informer ( ) . GetStore ( ) . Add ( test . backup ) )
2018-09-26 22:18:45 +00:00
}
err := c . processBackup ( test . key )
2019-04-24 17:54:43 +00:00
assert . Nil ( t , err )
2018-09-26 22:18:45 +00:00
// Any backup that would actually proceed to validation will cause a segfault because this
// test hasn't set up the necessary controller dependencies for validation/etc. So the lack
// of segfaults during test execution here imply that backups are not being processed, which
// is what we expect.
} )
}
}
func TestProcessBackupValidationFailures ( t * testing . T ) {
2019-07-31 14:46:48 +00:00
defaultBackupLocation := builder . ForBackupStorageLocation ( "velero" , "loc-1" ) . Result ( )
2018-09-26 22:18:45 +00:00
tests := [ ] struct {
name string
2019-06-21 21:08:08 +00:00
backup * velerov1api . Backup
backupLocation * velerov1api . BackupStorageLocation
2018-09-26 22:18:45 +00:00
expectedErrs [ ] string
} {
2018-08-16 22:41:59 +00:00
{
2018-09-26 22:18:45 +00:00
name : "invalid included/excluded resources fails validation" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . IncludedResources ( "foo" ) . ExcludedResources ( "foo" ) . Result ( ) ,
2018-09-26 22:18:45 +00:00
backupLocation : defaultBackupLocation ,
expectedErrs : [ ] string { "Invalid included/excluded resource lists: excludes list cannot contain an item in the includes list: foo" } ,
2018-08-16 22:41:59 +00:00
} ,
{
2018-09-26 22:18:45 +00:00
name : "invalid included/excluded namespaces fails validation" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . IncludedNamespaces ( "foo" ) . ExcludedNamespaces ( "foo" ) . Result ( ) ,
2018-09-26 22:18:45 +00:00
backupLocation : defaultBackupLocation ,
expectedErrs : [ ] string { "Invalid included/excluded namespace lists: excludes list cannot contain an item in the includes list: foo" } ,
2018-08-16 22:41:59 +00:00
} ,
{
2018-09-26 22:18:45 +00:00
name : "non-existent backup location fails validation" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . StorageLocation ( "nonexistent" ) . Result ( ) ,
2019-01-25 03:33:07 +00:00
expectedErrs : [ ] string { "a BackupStorageLocation CRD with the name specified in the backup spec needs to be created before this backup can be executed. Error: backupstoragelocation.velero.io \"nonexistent\" not found" } ,
2018-08-16 22:41:59 +00:00
} ,
2019-05-29 18:21:25 +00:00
{
name : "backup for read-only backup location fails validation" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . StorageLocation ( "read-only" ) . Result ( ) ,
backupLocation : builder . ForBackupStorageLocation ( "velero" , "read-only" ) . AccessMode ( velerov1api . BackupStorageLocationAccessModeReadOnly ) . Result ( ) ,
2019-05-29 18:21:25 +00:00
expectedErrs : [ ] string { "backup can't be created because backup storage location read-only is currently in read-only mode" } ,
} ,
2017-08-02 17:27:17 +00:00
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
2019-07-30 23:29:34 +00:00
formatFlag := logging . FormatText
2017-09-14 21:27:31 +00:00
var (
2018-09-26 22:18:45 +00:00
clientset = fake . NewSimpleClientset ( test . backup )
sharedInformers = informers . NewSharedInformerFactory ( clientset , 0 )
2019-07-30 23:29:34 +00:00
logger = logging . DefaultLogger ( logrus . DebugLevel , formatFlag )
2017-09-14 21:27:31 +00:00
)
2018-08-20 23:29:54 +00:00
2018-09-26 22:18:45 +00:00
c := & backupController {
2018-10-26 15:32:46 +00:00
genericController : newGenericController ( "backup-test" , logger ) ,
2019-01-25 03:33:07 +00:00
client : clientset . VeleroV1 ( ) ,
lister : sharedInformers . Velero ( ) . V1 ( ) . Backups ( ) . Lister ( ) ,
backupLocationLister : sharedInformers . Velero ( ) . V1 ( ) . BackupStorageLocations ( ) . Lister ( ) ,
snapshotLocationLister : sharedInformers . Velero ( ) . V1 ( ) . VolumeSnapshotLocations ( ) . Lister ( ) ,
2018-10-26 15:32:46 +00:00
defaultBackupLocation : defaultBackupLocation . Name ,
2019-04-04 21:59:13 +00:00
clock : & clock . RealClock { } ,
2019-07-30 23:29:34 +00:00
formatFlag : formatFlag ,
2018-09-26 22:18:45 +00:00
}
2017-08-02 17:27:17 +00:00
2018-09-26 22:18:45 +00:00
require . NotNil ( t , test . backup )
2019-01-25 03:33:07 +00:00
require . NoError ( t , sharedInformers . Velero ( ) . V1 ( ) . Backups ( ) . Informer ( ) . GetStore ( ) . Add ( test . backup ) )
2017-08-02 17:27:17 +00:00
2018-09-26 22:18:45 +00:00
if test . backupLocation != nil {
2019-01-25 03:33:07 +00:00
_ , err := clientset . VeleroV1 ( ) . BackupStorageLocations ( test . backupLocation . Namespace ) . Create ( test . backupLocation )
2018-09-26 22:18:45 +00:00
require . NoError ( t , err )
2018-06-20 18:08:07 +00:00
2019-01-25 03:33:07 +00:00
require . NoError ( t , sharedInformers . Velero ( ) . V1 ( ) . BackupStorageLocations ( ) . Informer ( ) . GetStore ( ) . Add ( test . backupLocation ) )
2018-05-13 13:28:09 +00:00
}
2018-09-26 22:18:45 +00:00
require . NoError ( t , c . processBackup ( fmt . Sprintf ( "%s/%s" , test . backup . Namespace , test . backup . Name ) ) )
2017-08-02 17:27:17 +00:00
2019-01-25 03:33:07 +00:00
res , err := clientset . VeleroV1 ( ) . Backups ( test . backup . Namespace ) . Get ( test . backup . Name , metav1 . GetOptions { } )
2018-09-26 22:18:45 +00:00
require . NoError ( t , err )
2018-07-10 22:17:53 +00:00
2019-06-21 21:08:08 +00:00
assert . Equal ( t , velerov1api . BackupPhaseFailedValidation , res . Status . Phase )
2018-09-26 22:18:45 +00:00
assert . Equal ( t , test . expectedErrs , res . Status . ValidationErrors )
2017-08-02 17:27:17 +00:00
2018-09-26 22:18:45 +00:00
// Any backup that would actually proceed to processing will cause a segfault because this
// test hasn't set up the necessary controller dependencies for running backups. So the lack
// of segfaults during test execution here imply that backups are not being processed, which
// is what we expect.
} )
}
}
2017-12-11 22:10:52 +00:00
2019-04-23 23:58:59 +00:00
func TestBackupLocationLabel ( t * testing . T ) {
tests := [ ] struct {
name string
2019-06-21 21:08:08 +00:00
backup * velerov1api . Backup
backupLocation * velerov1api . BackupStorageLocation
2019-04-23 23:58:59 +00:00
expectedBackupLocation string
} {
{
name : "valid backup location name should be used as a label" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . Result ( ) ,
backupLocation : builder . ForBackupStorageLocation ( "velero" , "loc-1" ) . Result ( ) ,
2019-04-23 23:58:59 +00:00
expectedBackupLocation : "loc-1" ,
} ,
{
2019-07-31 14:46:48 +00:00
name : "invalid storage location name should be handled while creating label" ,
backup : defaultBackup ( ) . Result ( ) ,
backupLocation : builder . ForBackupStorageLocation ( "velero" , "defaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefault" ) . Result ( ) ,
2019-04-23 23:58:59 +00:00
expectedBackupLocation : "defaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultd58343f" ,
} ,
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
2019-07-30 23:29:34 +00:00
formatFlag := logging . FormatText
2019-04-23 23:58:59 +00:00
var (
clientset = fake . NewSimpleClientset ( test . backup )
sharedInformers = informers . NewSharedInformerFactory ( clientset , 0 )
2019-07-30 23:29:34 +00:00
logger = logging . DefaultLogger ( logrus . DebugLevel , formatFlag )
2019-04-23 23:58:59 +00:00
)
c := & backupController {
genericController : newGenericController ( "backup-test" , logger ) ,
client : clientset . VeleroV1 ( ) ,
lister : sharedInformers . Velero ( ) . V1 ( ) . Backups ( ) . Lister ( ) ,
backupLocationLister : sharedInformers . Velero ( ) . V1 ( ) . BackupStorageLocations ( ) . Lister ( ) ,
snapshotLocationLister : sharedInformers . Velero ( ) . V1 ( ) . VolumeSnapshotLocations ( ) . Lister ( ) ,
defaultBackupLocation : test . backupLocation . Name ,
clock : & clock . RealClock { } ,
2019-07-30 23:29:34 +00:00
formatFlag : formatFlag ,
2019-04-23 23:58:59 +00:00
}
res := c . prepareBackupRequest ( test . backup )
assert . NotNil ( t , res )
assert . Equal ( t , test . expectedBackupLocation , res . Labels [ velerov1api . StorageLocationLabel ] )
} )
}
}
2019-04-04 21:59:13 +00:00
func TestDefaultBackupTTL ( t * testing . T ) {
var (
defaultBackupTTL = metav1 . Duration { Duration : 24 * 30 * time . Hour }
)
now , err := time . Parse ( time . RFC1123Z , time . RFC1123Z )
require . NoError ( t , err )
now = now . Local ( )
tests := [ ] struct {
name string
2019-06-21 21:08:08 +00:00
backup * velerov1api . Backup
backupLocation * velerov1api . BackupStorageLocation
2019-04-04 21:59:13 +00:00
expectedTTL metav1 . Duration
expectedExpiration metav1 . Time
} {
{
name : "backup with no TTL specified" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . Result ( ) ,
2019-04-04 21:59:13 +00:00
expectedTTL : defaultBackupTTL ,
expectedExpiration : metav1 . NewTime ( now . Add ( defaultBackupTTL . Duration ) ) ,
} ,
{
name : "backup with TTL specified" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . TTL ( time . Hour ) . Result ( ) ,
2019-04-04 21:59:13 +00:00
expectedTTL : metav1 . Duration { Duration : 1 * time . Hour } ,
expectedExpiration : metav1 . NewTime ( now . Add ( 1 * time . Hour ) ) ,
} ,
}
for _ , test := range tests {
2019-07-30 23:29:34 +00:00
formatFlag := logging . FormatText
2019-04-04 21:59:13 +00:00
var (
clientset = fake . NewSimpleClientset ( test . backup )
2019-07-30 23:29:34 +00:00
logger = logging . DefaultLogger ( logrus . DebugLevel , formatFlag )
2019-04-04 21:59:13 +00:00
sharedInformers = informers . NewSharedInformerFactory ( clientset , 0 )
)
t . Run ( test . name , func ( t * testing . T ) {
c := & backupController {
genericController : newGenericController ( "backup-test" , logger ) ,
backupLocationLister : sharedInformers . Velero ( ) . V1 ( ) . BackupStorageLocations ( ) . Lister ( ) ,
snapshotLocationLister : sharedInformers . Velero ( ) . V1 ( ) . VolumeSnapshotLocations ( ) . Lister ( ) ,
defaultBackupTTL : defaultBackupTTL . Duration ,
clock : clock . NewFakeClock ( now ) ,
2019-07-30 23:29:34 +00:00
formatFlag : formatFlag ,
2019-04-04 21:59:13 +00:00
}
res := c . prepareBackupRequest ( test . backup )
assert . NotNil ( t , res )
assert . Equal ( t , test . expectedTTL , res . Spec . TTL )
2019-10-14 16:20:28 +00:00
assert . Equal ( t , test . expectedExpiration , * res . Status . Expiration )
2019-04-04 21:59:13 +00:00
} )
}
}
2018-09-26 22:18:45 +00:00
func TestProcessBackupCompletions ( t * testing . T ) {
2019-07-31 15:27:12 +00:00
defaultBackupLocation := builder . ForBackupStorageLocation ( "velero" , "loc-1" ) . Bucket ( "store-1" ) . Result ( )
2017-12-11 22:10:52 +00:00
2018-09-26 22:18:45 +00:00
now , err := time . Parse ( time . RFC1123Z , time . RFC1123Z )
require . NoError ( t , err )
now = now . Local ( )
2019-10-14 16:20:28 +00:00
timestamp := metav1 . NewTime ( now )
2017-12-11 22:10:52 +00:00
2018-09-26 22:18:45 +00:00
tests := [ ] struct {
2019-04-23 23:19:00 +00:00
name string
2019-06-21 21:08:08 +00:00
backup * velerov1api . Backup
backupLocation * velerov1api . BackupStorageLocation
expectedResult * velerov1api . Backup
2019-04-23 23:19:00 +00:00
backupExists bool
existenceCheckError error
2018-09-26 22:18:45 +00:00
} {
2019-04-23 23:19:00 +00:00
// Completed
2018-09-26 22:18:45 +00:00
{
name : "backup with no backup location gets the default" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . Result ( ) ,
2018-09-26 22:18:45 +00:00
backupLocation : defaultBackupLocation ,
2019-06-21 21:08:08 +00:00
expectedResult : & velerov1api . Backup {
TypeMeta : metav1 . TypeMeta {
Kind : "Backup" ,
APIVersion : "velero.io/v1" ,
} ,
2018-09-26 22:18:45 +00:00
ObjectMeta : metav1 . ObjectMeta {
2019-06-21 21:08:08 +00:00
Namespace : velerov1api . DefaultNamespace ,
2018-09-26 22:18:45 +00:00
Name : "backup-1" ,
Labels : map [ string ] string {
2019-01-25 03:33:07 +00:00
"velero.io/storage-location" : "loc-1" ,
2018-09-26 22:18:45 +00:00
} ,
} ,
2019-06-21 21:08:08 +00:00
Spec : velerov1api . BackupSpec {
2018-09-26 22:18:45 +00:00
StorageLocation : defaultBackupLocation . Name ,
} ,
2019-06-21 21:08:08 +00:00
Status : velerov1api . BackupStatus {
Phase : velerov1api . BackupPhaseCompleted ,
2018-09-26 22:18:45 +00:00
Version : 1 ,
2019-10-14 16:20:28 +00:00
StartTimestamp : & timestamp ,
CompletionTimestamp : & timestamp ,
Expiration : & timestamp ,
2018-09-26 22:18:45 +00:00
} ,
} ,
} ,
{
name : "backup with a specific backup location keeps it" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . StorageLocation ( "alt-loc" ) . Result ( ) ,
2019-07-31 15:27:12 +00:00
backupLocation : builder . ForBackupStorageLocation ( "velero" , "alt-loc" ) . Bucket ( "store-1" ) . Result ( ) ,
2019-06-21 21:08:08 +00:00
expectedResult : & velerov1api . Backup {
TypeMeta : metav1 . TypeMeta {
Kind : "Backup" ,
APIVersion : "velero.io/v1" ,
} ,
2018-09-26 22:18:45 +00:00
ObjectMeta : metav1 . ObjectMeta {
2019-06-21 21:08:08 +00:00
Namespace : velerov1api . DefaultNamespace ,
2018-09-26 22:18:45 +00:00
Name : "backup-1" ,
Labels : map [ string ] string {
2019-01-25 03:33:07 +00:00
"velero.io/storage-location" : "alt-loc" ,
2018-09-26 22:18:45 +00:00
} ,
} ,
2019-06-21 21:08:08 +00:00
Spec : velerov1api . BackupSpec {
2018-09-26 22:18:45 +00:00
StorageLocation : "alt-loc" ,
} ,
2019-06-21 21:08:08 +00:00
Status : velerov1api . BackupStatus {
Phase : velerov1api . BackupPhaseCompleted ,
2018-09-26 22:18:45 +00:00
Version : 1 ,
2019-10-14 16:20:28 +00:00
StartTimestamp : & timestamp ,
CompletionTimestamp : & timestamp ,
Expiration : & timestamp ,
2018-09-26 22:18:45 +00:00
} ,
} ,
} ,
2019-05-29 18:21:25 +00:00
{
name : "backup for a location with ReadWrite access mode gets processed" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . StorageLocation ( "read-write" ) . Result ( ) ,
backupLocation : builder . ForBackupStorageLocation ( "velero" , "read-write" ) .
2019-07-31 15:27:12 +00:00
Bucket ( "store-1" ) .
2019-07-31 14:46:48 +00:00
AccessMode ( velerov1api . BackupStorageLocationAccessModeReadWrite ) .
Result ( ) ,
2019-06-21 21:08:08 +00:00
expectedResult : & velerov1api . Backup {
TypeMeta : metav1 . TypeMeta {
Kind : "Backup" ,
APIVersion : "velero.io/v1" ,
} ,
2019-05-29 18:21:25 +00:00
ObjectMeta : metav1 . ObjectMeta {
2019-06-21 21:08:08 +00:00
Namespace : velerov1api . DefaultNamespace ,
2019-05-29 18:21:25 +00:00
Name : "backup-1" ,
Labels : map [ string ] string {
"velero.io/storage-location" : "read-write" ,
} ,
} ,
2019-06-21 21:08:08 +00:00
Spec : velerov1api . BackupSpec {
2019-05-29 18:21:25 +00:00
StorageLocation : "read-write" ,
} ,
2019-06-21 21:08:08 +00:00
Status : velerov1api . BackupStatus {
Phase : velerov1api . BackupPhaseCompleted ,
2019-05-29 18:21:25 +00:00
Version : 1 ,
2019-10-14 16:20:28 +00:00
StartTimestamp : & timestamp ,
CompletionTimestamp : & timestamp ,
Expiration : & timestamp ,
2019-05-29 18:21:25 +00:00
} ,
} ,
} ,
2018-09-26 22:18:45 +00:00
{
name : "backup with a TTL has expiration set" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . TTL ( 10 * time . Minute ) . Result ( ) ,
2018-09-26 22:18:45 +00:00
backupLocation : defaultBackupLocation ,
2019-06-21 21:08:08 +00:00
expectedResult : & velerov1api . Backup {
TypeMeta : metav1 . TypeMeta {
Kind : "Backup" ,
APIVersion : "velero.io/v1" ,
} ,
2018-09-26 22:18:45 +00:00
ObjectMeta : metav1 . ObjectMeta {
2019-06-21 21:08:08 +00:00
Namespace : velerov1api . DefaultNamespace ,
2018-09-26 22:18:45 +00:00
Name : "backup-1" ,
Labels : map [ string ] string {
2019-01-25 03:33:07 +00:00
"velero.io/storage-location" : "loc-1" ,
2018-09-26 22:18:45 +00:00
} ,
} ,
2019-06-21 21:08:08 +00:00
Spec : velerov1api . BackupSpec {
2018-09-26 22:18:45 +00:00
TTL : metav1 . Duration { Duration : 10 * time . Minute } ,
StorageLocation : defaultBackupLocation . Name ,
} ,
2019-06-21 21:08:08 +00:00
Status : velerov1api . BackupStatus {
Phase : velerov1api . BackupPhaseCompleted ,
2018-09-26 22:18:45 +00:00
Version : 1 ,
2019-10-14 16:20:28 +00:00
Expiration : & metav1 . Time { now . Add ( 10 * time . Minute ) } ,
StartTimestamp : & timestamp ,
CompletionTimestamp : & timestamp ,
2018-09-26 22:18:45 +00:00
} ,
} ,
} ,
2019-04-23 23:19:00 +00:00
{
2019-04-24 17:54:43 +00:00
name : "backup without an existing backup will succeed" ,
2019-04-23 23:19:00 +00:00
backupExists : false ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . Result ( ) ,
2019-04-23 23:19:00 +00:00
backupLocation : defaultBackupLocation ,
2019-06-21 21:08:08 +00:00
expectedResult : & velerov1api . Backup {
TypeMeta : metav1 . TypeMeta {
Kind : "Backup" ,
APIVersion : "velero.io/v1" ,
} ,
2019-04-23 23:19:00 +00:00
ObjectMeta : metav1 . ObjectMeta {
2019-06-21 21:08:08 +00:00
Namespace : velerov1api . DefaultNamespace ,
2019-04-23 23:19:00 +00:00
Name : "backup-1" ,
Labels : map [ string ] string {
"velero.io/storage-location" : "loc-1" ,
} ,
} ,
2019-06-21 21:08:08 +00:00
Spec : velerov1api . BackupSpec {
2019-04-23 23:19:00 +00:00
StorageLocation : defaultBackupLocation . Name ,
} ,
2019-06-21 21:08:08 +00:00
Status : velerov1api . BackupStatus {
Phase : velerov1api . BackupPhaseCompleted ,
2019-04-23 23:19:00 +00:00
Version : 1 ,
2019-10-14 16:20:28 +00:00
StartTimestamp : & timestamp ,
CompletionTimestamp : & timestamp ,
Expiration : & timestamp ,
2019-04-23 23:19:00 +00:00
} ,
} ,
} ,
// Failed
{
name : "backup with existing backup will fail" ,
backupExists : true ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . Result ( ) ,
2019-04-23 23:19:00 +00:00
backupLocation : defaultBackupLocation ,
2019-06-21 21:08:08 +00:00
expectedResult : & velerov1api . Backup {
TypeMeta : metav1 . TypeMeta {
Kind : "Backup" ,
APIVersion : "velero.io/v1" ,
} ,
2019-04-23 23:19:00 +00:00
ObjectMeta : metav1 . ObjectMeta {
2019-06-21 21:08:08 +00:00
Namespace : velerov1api . DefaultNamespace ,
2019-04-23 23:19:00 +00:00
Name : "backup-1" ,
Labels : map [ string ] string {
"velero.io/storage-location" : "loc-1" ,
} ,
} ,
2019-06-21 21:08:08 +00:00
Spec : velerov1api . BackupSpec {
2019-04-23 23:19:00 +00:00
StorageLocation : defaultBackupLocation . Name ,
} ,
2019-06-21 21:08:08 +00:00
Status : velerov1api . BackupStatus {
Phase : velerov1api . BackupPhaseFailed ,
2019-04-23 23:19:00 +00:00
Version : 1 ,
2019-10-14 16:20:28 +00:00
StartTimestamp : & timestamp ,
CompletionTimestamp : & timestamp ,
Expiration : & timestamp ,
2019-04-23 23:19:00 +00:00
} ,
} ,
} ,
{
name : "error when checking if backup exists will cause backup to fail" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . Result ( ) ,
2019-04-23 23:19:00 +00:00
existenceCheckError : errors . New ( "Backup already exists in object storage" ) ,
backupLocation : defaultBackupLocation ,
2019-06-21 21:08:08 +00:00
expectedResult : & velerov1api . Backup {
TypeMeta : metav1 . TypeMeta {
Kind : "Backup" ,
APIVersion : "velero.io/v1" ,
} ,
2019-04-23 23:19:00 +00:00
ObjectMeta : metav1 . ObjectMeta {
2019-06-21 21:08:08 +00:00
Namespace : velerov1api . DefaultNamespace ,
2019-04-23 23:19:00 +00:00
Name : "backup-1" ,
Labels : map [ string ] string {
"velero.io/storage-location" : "loc-1" ,
} ,
} ,
2019-06-21 21:08:08 +00:00
Spec : velerov1api . BackupSpec {
2019-04-23 23:19:00 +00:00
StorageLocation : defaultBackupLocation . Name ,
} ,
2019-06-21 21:08:08 +00:00
Status : velerov1api . BackupStatus {
Phase : velerov1api . BackupPhaseFailed ,
2019-04-23 23:19:00 +00:00
Version : 1 ,
2019-10-14 16:20:28 +00:00
StartTimestamp : & timestamp ,
CompletionTimestamp : & timestamp ,
Expiration : & timestamp ,
2019-04-23 23:19:00 +00:00
} ,
} ,
} ,
2018-09-26 22:18:45 +00:00
}
2017-12-11 22:10:52 +00:00
2018-09-26 22:18:45 +00:00
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
2019-07-30 23:29:34 +00:00
formatFlag := logging . FormatText
2018-09-26 22:18:45 +00:00
var (
clientset = fake . NewSimpleClientset ( test . backup )
sharedInformers = informers . NewSharedInformerFactory ( clientset , 0 )
2019-07-30 23:29:34 +00:00
logger = logging . DefaultLogger ( logrus . DebugLevel , formatFlag )
2018-09-26 22:18:45 +00:00
pluginManager = new ( pluginmocks . Manager )
backupStore = new ( persistencemocks . BackupStore )
backupper = new ( fakeBackupper )
)
2018-06-20 18:08:07 +00:00
2018-09-26 22:18:45 +00:00
c := & backupController {
2018-10-26 15:32:46 +00:00
genericController : newGenericController ( "backup-test" , logger ) ,
2019-01-25 03:33:07 +00:00
client : clientset . VeleroV1 ( ) ,
lister : sharedInformers . Velero ( ) . V1 ( ) . Backups ( ) . Lister ( ) ,
backupLocationLister : sharedInformers . Velero ( ) . V1 ( ) . BackupStorageLocations ( ) . Lister ( ) ,
snapshotLocationLister : sharedInformers . Velero ( ) . V1 ( ) . VolumeSnapshotLocations ( ) . Lister ( ) ,
2018-10-26 15:32:46 +00:00
defaultBackupLocation : defaultBackupLocation . Name ,
backupTracker : NewBackupTracker ( ) ,
metrics : metrics . NewServerMetrics ( ) ,
clock : clock . NewFakeClock ( now ) ,
2019-03-15 18:32:11 +00:00
newPluginManager : func ( logrus . FieldLogger ) clientmgmt . Manager { return pluginManager } ,
2019-06-21 21:08:08 +00:00
newBackupStore : func ( * velerov1api . BackupStorageLocation , persistence . ObjectStoreGetter , logrus . FieldLogger ) ( persistence . BackupStore , error ) {
2018-09-26 22:18:45 +00:00
return backupStore , nil
} ,
2019-07-30 23:29:34 +00:00
backupper : backupper ,
formatFlag : formatFlag ,
2018-09-26 22:18:45 +00:00
}
2017-08-02 17:27:17 +00:00
2018-09-26 22:18:45 +00:00
pluginManager . On ( "GetBackupItemActions" ) . Return ( nil , nil )
pluginManager . On ( "CleanupClients" ) . Return ( nil )
2019-03-14 20:35:06 +00:00
backupper . On ( "Backup" , mock . Anything , mock . Anything , mock . Anything , [ ] velero . BackupItemAction ( nil ) , pluginManager ) . Return ( nil )
2019-07-24 19:51:20 +00:00
backupStore . On ( "BackupExists" , test . backupLocation . Spec . StorageType . ObjectStorage . Bucket , test . backup . Name ) . Return ( test . backupExists , test . existenceCheckError )
2017-08-02 17:27:17 +00:00
2019-07-24 19:51:20 +00:00
// Ensure we have a CompletionTimestamp when uploading and that the backup name matches the backup in the object store.
2018-09-26 22:18:45 +00:00
// Failures will display the bytes in buf.
2019-07-24 19:51:20 +00:00
hasNameAndCompletionTimestamp := func ( info persistence . BackupInfo ) bool {
buf := new ( bytes . Buffer )
buf . ReadFrom ( info . Metadata )
return info . Name == test . backup . Name &&
strings . Contains ( buf . String ( ) , ` "completionTimestamp": "2006-01-02T22:04:05Z" ` )
2017-08-02 17:27:17 +00:00
}
2019-07-24 19:51:20 +00:00
backupStore . On ( "PutBackup" , mock . MatchedBy ( hasNameAndCompletionTimestamp ) ) . Return ( nil )
2017-08-02 17:27:17 +00:00
2018-09-26 22:18:45 +00:00
// add the test's backup to the informer/lister store
require . NotNil ( t , test . backup )
2019-01-25 03:33:07 +00:00
require . NoError ( t , sharedInformers . Velero ( ) . V1 ( ) . Backups ( ) . Informer ( ) . GetStore ( ) . Add ( test . backup ) )
2017-12-11 22:10:52 +00:00
2018-09-26 22:18:45 +00:00
// add the default backup storage location to the clientset and the informer/lister store
2019-01-25 03:33:07 +00:00
_ , err := clientset . VeleroV1 ( ) . BackupStorageLocations ( defaultBackupLocation . Namespace ) . Create ( defaultBackupLocation )
2018-09-26 22:18:45 +00:00
require . NoError ( t , err )
2017-12-11 22:10:52 +00:00
2019-01-25 03:33:07 +00:00
require . NoError ( t , sharedInformers . Velero ( ) . V1 ( ) . BackupStorageLocations ( ) . Informer ( ) . GetStore ( ) . Add ( defaultBackupLocation ) )
2017-12-11 22:10:52 +00:00
2018-09-26 22:18:45 +00:00
// add the test's backup storage location to the clientset and the informer/lister store
// if it's different than the default
if test . backupLocation != nil && test . backupLocation != defaultBackupLocation {
2019-01-25 03:33:07 +00:00
_ , err := clientset . VeleroV1 ( ) . BackupStorageLocations ( test . backupLocation . Namespace ) . Create ( test . backupLocation )
2018-09-26 22:18:45 +00:00
require . NoError ( t , err )
2017-12-11 22:10:52 +00:00
2019-01-25 03:33:07 +00:00
require . NoError ( t , sharedInformers . Velero ( ) . V1 ( ) . BackupStorageLocations ( ) . Informer ( ) . GetStore ( ) . Add ( test . backupLocation ) )
2017-08-02 17:27:17 +00:00
}
2018-09-26 22:18:45 +00:00
require . NoError ( t , c . processBackup ( fmt . Sprintf ( "%s/%s" , test . backup . Namespace , test . backup . Name ) ) )
2017-12-11 22:10:52 +00:00
2019-01-25 03:33:07 +00:00
res , err := clientset . VeleroV1 ( ) . Backups ( test . backup . Namespace ) . Get ( test . backup . Name , metav1 . GetOptions { } )
2018-09-26 22:18:45 +00:00
require . NoError ( t , err )
2017-12-11 22:10:52 +00:00
2018-09-26 22:18:45 +00:00
assert . Equal ( t , test . expectedResult , res )
2017-08-02 17:27:17 +00:00
} )
}
}
2018-09-25 14:51:28 +00:00
2018-09-26 22:18:45 +00:00
func TestValidateAndGetSnapshotLocations ( t * testing . T ) {
2018-09-25 14:51:28 +00:00
tests := [ ] struct {
name string
2019-06-21 21:08:08 +00:00
backup * velerov1api . Backup
2019-07-31 14:46:48 +00:00
locations [ ] * velerov1api . VolumeSnapshotLocation
2018-10-26 15:32:46 +00:00
defaultLocations map [ string ] string
2018-09-25 14:51:28 +00:00
expectedVolumeSnapshotLocationNames [ ] string // adding these in the expected order will allow to test with better msgs in case of a test failure
expectedErrors string
expectedSuccess bool
} {
{
2018-10-26 15:32:46 +00:00
name : "location name does not correspond to any existing location" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . Phase ( velerov1api . BackupPhaseNew ) . VolumeSnapshotLocations ( "random-name" ) . Result ( ) ,
locations : [ ] * velerov1api . VolumeSnapshotLocation {
builder . ForVolumeSnapshotLocation ( velerov1api . DefaultNamespace , "aws-us-east-1" ) . Provider ( "aws" ) . Result ( ) ,
builder . ForVolumeSnapshotLocation ( velerov1api . DefaultNamespace , "aws-us-west-1" ) . Provider ( "aws" ) . Result ( ) ,
builder . ForVolumeSnapshotLocation ( velerov1api . DefaultNamespace , "some-name" ) . Provider ( "fake-provider" ) . Result ( ) ,
2018-10-26 15:32:46 +00:00
} ,
2019-01-25 03:33:07 +00:00
expectedErrors : "a VolumeSnapshotLocation CRD for the location random-name with the name specified in the backup spec needs to be created before this snapshot can be executed. Error: volumesnapshotlocation.velero.io \"random-name\" not found" , expectedSuccess : false ,
2018-09-25 14:51:28 +00:00
} ,
{
2018-10-26 15:32:46 +00:00
name : "duplicate locationName per provider: should filter out dups" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . Phase ( velerov1api . BackupPhaseNew ) . VolumeSnapshotLocations ( "aws-us-west-1" , "aws-us-west-1" ) . Result ( ) ,
locations : [ ] * velerov1api . VolumeSnapshotLocation {
builder . ForVolumeSnapshotLocation ( velerov1api . DefaultNamespace , "aws-us-east-1" ) . Provider ( "aws" ) . Result ( ) ,
builder . ForVolumeSnapshotLocation ( velerov1api . DefaultNamespace , "aws-us-west-1" ) . Provider ( "aws" ) . Result ( ) ,
2018-10-26 15:32:46 +00:00
} ,
expectedVolumeSnapshotLocationNames : [ ] string { "aws-us-west-1" } ,
2018-09-25 14:51:28 +00:00
expectedSuccess : true ,
} ,
{
2018-10-26 15:32:46 +00:00
name : "multiple non-dupe location names per provider should error" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . Phase ( velerov1api . BackupPhaseNew ) . VolumeSnapshotLocations ( "aws-us-east-1" , "aws-us-west-1" ) . Result ( ) ,
locations : [ ] * velerov1api . VolumeSnapshotLocation {
builder . ForVolumeSnapshotLocation ( velerov1api . DefaultNamespace , "aws-us-east-1" ) . Provider ( "aws" ) . Result ( ) ,
builder . ForVolumeSnapshotLocation ( velerov1api . DefaultNamespace , "aws-us-west-1" ) . Provider ( "aws" ) . Result ( ) ,
builder . ForVolumeSnapshotLocation ( velerov1api . DefaultNamespace , "some-name" ) . Provider ( "fake-provider" ) . Result ( ) ,
2018-10-26 15:32:46 +00:00
} ,
expectedErrors : "more than one VolumeSnapshotLocation name specified for provider aws: aws-us-west-1; unexpected name was aws-us-east-1" ,
2018-09-25 14:51:28 +00:00
expectedSuccess : false ,
} ,
{
2018-10-26 15:32:46 +00:00
name : "no location name for the provider exists, only one VSL for the provider: use it" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . Phase ( velerov1api . BackupPhaseNew ) . Result ( ) ,
locations : [ ] * velerov1api . VolumeSnapshotLocation {
builder . ForVolumeSnapshotLocation ( velerov1api . DefaultNamespace , "aws-us-east-1" ) . Provider ( "aws" ) . Result ( ) ,
2018-10-26 15:32:46 +00:00
} ,
expectedVolumeSnapshotLocationNames : [ ] string { "aws-us-east-1" } ,
expectedSuccess : true ,
} ,
{
name : "no location name for the provider exists, no default, more than one VSL for the provider: error" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . Phase ( velerov1api . BackupPhaseNew ) . Result ( ) ,
locations : [ ] * velerov1api . VolumeSnapshotLocation {
builder . ForVolumeSnapshotLocation ( velerov1api . DefaultNamespace , "aws-us-east-1" ) . Provider ( "aws" ) . Result ( ) ,
builder . ForVolumeSnapshotLocation ( velerov1api . DefaultNamespace , "aws-us-west-1" ) . Provider ( "aws" ) . Result ( ) ,
2018-10-26 15:32:46 +00:00
} ,
expectedErrors : "provider aws has more than one possible volume snapshot location, and none were specified explicitly or as a default" ,
} ,
{
name : "no location name for the provider exists, more than one VSL for the provider: the provider's default should be added" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . Phase ( velerov1api . BackupPhaseNew ) . Result ( ) ,
2018-10-26 15:32:46 +00:00
defaultLocations : map [ string ] string { "aws" : "aws-us-east-1" } ,
2019-07-31 14:46:48 +00:00
locations : [ ] * velerov1api . VolumeSnapshotLocation {
builder . ForVolumeSnapshotLocation ( velerov1api . DefaultNamespace , "aws-us-east-1" ) . Provider ( "aws" ) . Result ( ) ,
builder . ForVolumeSnapshotLocation ( velerov1api . DefaultNamespace , "aws-us-west-1" ) . Provider ( "aws" ) . Result ( ) ,
2018-10-26 15:32:46 +00:00
} ,
expectedVolumeSnapshotLocationNames : [ ] string { "aws-us-east-1" } ,
2018-09-25 14:51:28 +00:00
expectedSuccess : true ,
} ,
{
name : "no existing location name and no default location name given" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . Phase ( velerov1api . BackupPhaseNew ) . Result ( ) ,
2018-09-25 14:51:28 +00:00
expectedSuccess : true ,
} ,
{
2018-10-26 15:32:46 +00:00
name : "multiple location names for a provider, default location name for another provider" ,
2019-07-31 14:46:48 +00:00
backup : defaultBackup ( ) . Phase ( velerov1api . BackupPhaseNew ) . VolumeSnapshotLocations ( "aws-us-west-1" , "aws-us-west-1" ) . Result ( ) ,
2018-10-26 15:32:46 +00:00
defaultLocations : map [ string ] string { "fake-provider" : "some-name" } ,
2019-07-31 14:46:48 +00:00
locations : [ ] * velerov1api . VolumeSnapshotLocation {
builder . ForVolumeSnapshotLocation ( velerov1api . DefaultNamespace , "aws-us-west-1" ) . Provider ( "aws" ) . Result ( ) ,
builder . ForVolumeSnapshotLocation ( velerov1api . DefaultNamespace , "some-name" ) . Provider ( "fake-provider" ) . Result ( ) ,
2018-10-26 15:32:46 +00:00
} ,
expectedVolumeSnapshotLocationNames : [ ] string { "aws-us-west-1" , "some-name" } ,
2018-09-25 14:51:28 +00:00
expectedSuccess : true ,
} ,
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
var (
client = fake . NewSimpleClientset ( )
sharedInformers = informers . NewSharedInformerFactory ( client , 0 )
)
c := & backupController {
2019-01-25 03:33:07 +00:00
snapshotLocationLister : sharedInformers . Velero ( ) . V1 ( ) . VolumeSnapshotLocations ( ) . Lister ( ) ,
2018-09-26 22:18:45 +00:00
defaultSnapshotLocations : test . defaultLocations ,
2018-09-25 14:51:28 +00:00
}
// set up a Backup object to represent what we expect to be passed to backupper.Backup()
backup := test . backup . DeepCopy ( )
backup . Spec . VolumeSnapshotLocations = test . backup . Spec . VolumeSnapshotLocations
for _ , location := range test . locations {
2019-07-31 14:46:48 +00:00
require . NoError ( t , sharedInformers . Velero ( ) . V1 ( ) . VolumeSnapshotLocations ( ) . Informer ( ) . GetStore ( ) . Add ( location ) )
2018-09-25 14:51:28 +00:00
}
2018-09-26 22:18:45 +00:00
providerLocations , errs := c . validateAndGetSnapshotLocations ( backup )
2018-09-25 14:51:28 +00:00
if test . expectedSuccess {
for _ , err := range errs {
2018-09-26 22:18:45 +00:00
require . NoError ( t , errors . New ( err ) , "validateAndGetSnapshotLocations unexpected error: %v" , err )
2018-09-25 14:51:28 +00:00
}
2018-09-26 22:18:45 +00:00
var locations [ ] string
for _ , loc := range providerLocations {
locations = append ( locations , loc . Name )
}
sort . Strings ( test . expectedVolumeSnapshotLocationNames )
sort . Strings ( locations )
require . Equal ( t , test . expectedVolumeSnapshotLocationNames , locations )
2018-09-25 14:51:28 +00:00
} else {
if len ( errs ) == 0 {
2018-09-26 22:18:45 +00:00
require . Error ( t , nil , "validateAndGetSnapshotLocations expected error" )
2018-09-25 14:51:28 +00:00
}
require . Contains ( t , errs , test . expectedErrors )
}
} )
}
}
2020-01-21 20:21:28 +00:00
// Test_getLastSuccessBySchedule verifies that the getLastSuccessBySchedule helper function correctly returns
// the completion timestamp of the most recent completed backup for each schedule, including an entry for ad-hoc
// or non-scheduled backups.
func Test_getLastSuccessBySchedule ( t * testing . T ) {
buildBackup := func ( phase velerov1api . BackupPhase , completion time . Time , schedule string ) * velerov1api . Backup {
b := builder . ForBackup ( "" , "" ) .
ObjectMeta ( builder . WithLabels ( velerov1api . ScheduleNameLabel , schedule ) ) .
Phase ( phase )
if ! completion . IsZero ( ) {
b . CompletionTimestamp ( completion )
}
return b . Result ( )
}
// create a static "base time" that can be used to easily construct completion timestamps
// by using the .Add(...) method.
baseTime , err := time . Parse ( time . RFC1123 , time . RFC1123 )
require . NoError ( t , err )
tests := [ ] struct {
name string
backups [ ] * velerov1api . Backup
want map [ string ] time . Time
} {
{
name : "when backups is nil, an empty map is returned" ,
backups : nil ,
want : map [ string ] time . Time { } ,
} ,
{
name : "when backups is empty, an empty map is returned" ,
backups : [ ] * velerov1api . Backup { } ,
want : map [ string ] time . Time { } ,
} ,
{
name : "when multiple completed backups for a schedule exist, the latest one is returned" ,
backups : [ ] * velerov1api . Backup {
buildBackup ( velerov1api . BackupPhaseCompleted , baseTime , "schedule-1" ) ,
buildBackup ( velerov1api . BackupPhaseCompleted , baseTime . Add ( time . Second ) , "schedule-1" ) ,
buildBackup ( velerov1api . BackupPhaseCompleted , baseTime . Add ( - time . Second ) , "schedule-1" ) ,
} ,
want : map [ string ] time . Time {
"schedule-1" : baseTime . Add ( time . Second ) ,
} ,
} ,
{
name : "when the most recent backup for a schedule is Failed, the timestamp of the most recent Completed one is returned" ,
backups : [ ] * velerov1api . Backup {
buildBackup ( velerov1api . BackupPhaseCompleted , baseTime , "schedule-1" ) ,
buildBackup ( velerov1api . BackupPhaseFailed , baseTime . Add ( time . Second ) , "schedule-1" ) ,
buildBackup ( velerov1api . BackupPhaseCompleted , baseTime . Add ( - time . Second ) , "schedule-1" ) ,
} ,
want : map [ string ] time . Time {
"schedule-1" : baseTime ,
} ,
} ,
{
name : "when there are no Completed backups for a schedule, it's not returned" ,
backups : [ ] * velerov1api . Backup {
buildBackup ( velerov1api . BackupPhaseInProgress , baseTime , "schedule-1" ) ,
buildBackup ( velerov1api . BackupPhaseFailed , baseTime . Add ( time . Second ) , "schedule-1" ) ,
buildBackup ( velerov1api . BackupPhasePartiallyFailed , baseTime . Add ( - time . Second ) , "schedule-1" ) ,
} ,
want : map [ string ] time . Time { } ,
} ,
{
name : "when backups exist without a schedule, the most recent Completed one is returned" ,
backups : [ ] * velerov1api . Backup {
buildBackup ( velerov1api . BackupPhaseCompleted , baseTime , "" ) ,
buildBackup ( velerov1api . BackupPhaseFailed , baseTime . Add ( time . Second ) , "" ) ,
buildBackup ( velerov1api . BackupPhaseCompleted , baseTime . Add ( - time . Second ) , "" ) ,
} ,
want : map [ string ] time . Time {
"" : baseTime ,
} ,
} ,
{
name : "when backups exist for multiple schedules, the most recent Completed timestamp for each schedule is returned" ,
backups : [ ] * velerov1api . Backup {
// ad-hoc backups (no schedule)
buildBackup ( velerov1api . BackupPhaseCompleted , baseTime . Add ( 30 * time . Minute ) , "" ) ,
buildBackup ( velerov1api . BackupPhaseFailed , baseTime . Add ( time . Hour ) , "" ) ,
buildBackup ( velerov1api . BackupPhaseCompleted , baseTime . Add ( - time . Second ) , "" ) ,
// schedule-1
buildBackup ( velerov1api . BackupPhaseCompleted , baseTime , "schedule-1" ) ,
buildBackup ( velerov1api . BackupPhaseFailed , baseTime . Add ( time . Second ) , "schedule-1" ) ,
buildBackup ( velerov1api . BackupPhaseCompleted , baseTime . Add ( - time . Second ) , "schedule-1" ) ,
// schedule-2
buildBackup ( velerov1api . BackupPhaseCompleted , baseTime . Add ( 24 * time . Hour ) , "schedule-2" ) ,
buildBackup ( velerov1api . BackupPhaseCompleted , baseTime . Add ( 48 * time . Hour ) , "schedule-2" ) ,
buildBackup ( velerov1api . BackupPhaseCompleted , baseTime . Add ( 72 * time . Hour ) , "schedule-2" ) ,
// schedule-3
buildBackup ( velerov1api . BackupPhaseNew , baseTime , "schedule-3" ) ,
buildBackup ( velerov1api . BackupPhaseInProgress , baseTime . Add ( time . Minute ) , "schedule-3" ) ,
buildBackup ( velerov1api . BackupPhasePartiallyFailed , baseTime . Add ( 2 * time . Minute ) , "schedule-3" ) ,
} ,
want : map [ string ] time . Time {
"" : baseTime . Add ( 30 * time . Minute ) ,
"schedule-1" : baseTime ,
"schedule-2" : baseTime . Add ( 72 * time . Hour ) ,
} ,
} ,
}
for _ , tc := range tests {
t . Run ( tc . name , func ( t * testing . T ) {
assert . Equal ( t , tc . want , getLastSuccessBySchedule ( tc . backups ) )
} )
}
}