2017-08-02 17:27:17 +00:00
|
|
|
/*
|
|
|
|
Copyright 2017 Heptio Inc.
|
|
|
|
|
|
|
|
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 (
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
|
|
|
"k8s.io/apimachinery/pkg/util/clock"
|
|
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
2017-08-29 20:28:51 +00:00
|
|
|
core "k8s.io/client-go/testing"
|
2017-08-02 17:27:17 +00:00
|
|
|
|
|
|
|
api "github.com/heptio/ark/pkg/apis/ark/v1"
|
2017-08-09 22:52:27 +00:00
|
|
|
"github.com/heptio/ark/pkg/cloudprovider"
|
2017-08-02 17:27:17 +00:00
|
|
|
"github.com/heptio/ark/pkg/generated/clientset/fake"
|
|
|
|
informers "github.com/heptio/ark/pkg/generated/informers/externalversions"
|
|
|
|
. "github.com/heptio/ark/pkg/util/test"
|
|
|
|
)
|
|
|
|
|
|
|
|
type gcTest struct {
|
2017-08-09 22:52:27 +00:00
|
|
|
name string
|
2017-09-06 20:58:43 +00:00
|
|
|
backups []*api.Backup
|
2017-08-09 22:52:27 +00:00
|
|
|
snapshots sets.String
|
|
|
|
nilSnapshotService bool
|
2017-08-02 17:27:17 +00:00
|
|
|
|
2017-09-06 20:58:43 +00:00
|
|
|
expectedDeletions sets.String
|
2017-08-02 17:27:17 +00:00
|
|
|
expectedSnapshotsRemaining sets.String
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGarbageCollect(t *testing.T) {
|
|
|
|
fakeClock := clock.NewFakeClock(time.Now())
|
|
|
|
|
|
|
|
tests := []gcTest{
|
|
|
|
gcTest{
|
2017-09-06 20:58:43 +00:00
|
|
|
name: "basic-expired",
|
|
|
|
backups: []*api.Backup{
|
|
|
|
NewTestBackup().WithName("backup-1").
|
|
|
|
WithExpiration(fakeClock.Now().Add(-1*time.Second)).
|
|
|
|
WithSnapshot("pv-1", "snapshot-1").
|
|
|
|
WithSnapshot("pv-2", "snapshot-2").
|
|
|
|
Backup,
|
2017-08-02 17:27:17 +00:00
|
|
|
},
|
|
|
|
snapshots: sets.NewString("snapshot-1", "snapshot-2"),
|
2017-09-06 20:58:43 +00:00
|
|
|
expectedDeletions: sets.NewString("backup-1"),
|
2017-08-02 17:27:17 +00:00
|
|
|
expectedSnapshotsRemaining: sets.NewString(),
|
|
|
|
},
|
|
|
|
gcTest{
|
2017-09-06 20:58:43 +00:00
|
|
|
name: "basic-unexpired",
|
|
|
|
backups: []*api.Backup{
|
|
|
|
NewTestBackup().WithName("backup-1").
|
|
|
|
WithExpiration(fakeClock.Now().Add(1*time.Minute)).
|
|
|
|
WithSnapshot("pv-1", "snapshot-1").
|
|
|
|
WithSnapshot("pv-2", "snapshot-2").
|
|
|
|
Backup,
|
2017-08-02 17:27:17 +00:00
|
|
|
},
|
2017-09-06 20:58:43 +00:00
|
|
|
snapshots: sets.NewString("snapshot-1", "snapshot-2"),
|
|
|
|
expectedDeletions: sets.NewString(),
|
2017-08-02 17:27:17 +00:00
|
|
|
expectedSnapshotsRemaining: sets.NewString("snapshot-1", "snapshot-2"),
|
|
|
|
},
|
|
|
|
gcTest{
|
2017-09-06 20:58:43 +00:00
|
|
|
name: "one expired, one unexpired",
|
|
|
|
backups: []*api.Backup{
|
|
|
|
NewTestBackup().WithName("backup-1").
|
|
|
|
WithExpiration(fakeClock.Now().Add(-1*time.Minute)).
|
|
|
|
WithSnapshot("pv-1", "snapshot-1").
|
|
|
|
WithSnapshot("pv-2", "snapshot-2").
|
|
|
|
Backup,
|
|
|
|
NewTestBackup().WithName("backup-2").
|
|
|
|
WithExpiration(fakeClock.Now().Add(1*time.Minute)).
|
|
|
|
WithSnapshot("pv-3", "snapshot-3").
|
|
|
|
WithSnapshot("pv-4", "snapshot-4").
|
|
|
|
Backup,
|
2017-08-02 17:27:17 +00:00
|
|
|
},
|
2017-09-06 20:58:43 +00:00
|
|
|
snapshots: sets.NewString("snapshot-1", "snapshot-2", "snapshot-3", "snapshot-4"),
|
|
|
|
expectedDeletions: sets.NewString("backup-1"),
|
2017-08-02 17:27:17 +00:00
|
|
|
expectedSnapshotsRemaining: sets.NewString("snapshot-3", "snapshot-4"),
|
|
|
|
},
|
|
|
|
gcTest{
|
2017-09-06 20:58:43 +00:00
|
|
|
name: "none expired in target bucket",
|
|
|
|
backups: []*api.Backup{
|
|
|
|
NewTestBackup().WithName("backup-2").
|
|
|
|
WithExpiration(fakeClock.Now().Add(1*time.Minute)).
|
|
|
|
WithSnapshot("pv-3", "snapshot-3").
|
|
|
|
WithSnapshot("pv-4", "snapshot-4").
|
|
|
|
Backup,
|
2017-08-02 17:27:17 +00:00
|
|
|
},
|
2017-09-06 20:58:43 +00:00
|
|
|
snapshots: sets.NewString("snapshot-1", "snapshot-2", "snapshot-3", "snapshot-4"),
|
|
|
|
expectedDeletions: sets.NewString(),
|
2017-08-02 17:27:17 +00:00
|
|
|
expectedSnapshotsRemaining: sets.NewString("snapshot-1", "snapshot-2", "snapshot-3", "snapshot-4"),
|
|
|
|
},
|
|
|
|
gcTest{
|
2017-09-06 20:58:43 +00:00
|
|
|
name: "orphan snapshots",
|
|
|
|
backups: []*api.Backup{
|
|
|
|
NewTestBackup().WithName("backup-1").
|
|
|
|
WithExpiration(fakeClock.Now().Add(-1*time.Minute)).
|
|
|
|
WithSnapshot("pv-1", "snapshot-1").
|
|
|
|
WithSnapshot("pv-2", "snapshot-2").
|
|
|
|
Backup,
|
2017-08-02 17:27:17 +00:00
|
|
|
},
|
|
|
|
snapshots: sets.NewString("snapshot-1", "snapshot-2", "snapshot-3", "snapshot-4"),
|
2017-09-06 20:58:43 +00:00
|
|
|
expectedDeletions: sets.NewString("backup-1"),
|
2017-08-02 17:27:17 +00:00
|
|
|
expectedSnapshotsRemaining: sets.NewString("snapshot-3", "snapshot-4"),
|
|
|
|
},
|
2017-08-09 22:52:27 +00:00
|
|
|
gcTest{
|
2017-09-06 20:58:43 +00:00
|
|
|
name: "no snapshot service only GC's backups without snapshots",
|
|
|
|
backups: []*api.Backup{
|
|
|
|
NewTestBackup().WithName("backup-1").
|
|
|
|
WithExpiration(fakeClock.Now().Add(-1*time.Second)).
|
|
|
|
WithSnapshot("pv-1", "snapshot-1").
|
|
|
|
WithSnapshot("pv-2", "snapshot-2").
|
|
|
|
Backup,
|
|
|
|
NewTestBackup().WithName("backup-2").
|
|
|
|
WithExpiration(fakeClock.Now().Add(-1 * time.Second)).
|
|
|
|
Backup,
|
2017-08-09 22:52:27 +00:00
|
|
|
},
|
|
|
|
snapshots: sets.NewString("snapshot-1", "snapshot-2"),
|
|
|
|
nilSnapshotService: true,
|
2017-09-06 20:58:43 +00:00
|
|
|
expectedDeletions: sets.NewString("backup-2"),
|
2017-08-09 22:52:27 +00:00
|
|
|
},
|
2017-08-02 17:27:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
2017-08-09 22:52:27 +00:00
|
|
|
var (
|
2017-09-06 20:58:43 +00:00
|
|
|
backupService = &BackupService{}
|
2017-08-09 22:52:27 +00:00
|
|
|
snapshotService *FakeSnapshotService
|
|
|
|
)
|
|
|
|
|
|
|
|
if !test.nilSnapshotService {
|
|
|
|
snapshotService = &FakeSnapshotService{SnapshotsTaken: test.snapshots}
|
|
|
|
}
|
2017-08-02 17:27:17 +00:00
|
|
|
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
var (
|
|
|
|
client = fake.NewSimpleClientset()
|
|
|
|
sharedInformers = informers.NewSharedInformerFactory(client, 0)
|
2017-08-09 22:52:27 +00:00
|
|
|
snapSvc cloudprovider.SnapshotService
|
2017-09-06 20:58:43 +00:00
|
|
|
bucket = "bucket"
|
2017-08-02 17:27:17 +00:00
|
|
|
)
|
|
|
|
|
2017-08-09 22:52:27 +00:00
|
|
|
if snapshotService != nil {
|
|
|
|
snapSvc = snapshotService
|
|
|
|
}
|
|
|
|
|
2017-08-02 17:27:17 +00:00
|
|
|
controller := NewGCController(
|
|
|
|
backupService,
|
2017-08-09 22:52:27 +00:00
|
|
|
snapSvc,
|
2017-09-06 20:58:43 +00:00
|
|
|
bucket,
|
2017-08-02 17:27:17 +00:00
|
|
|
1*time.Millisecond,
|
|
|
|
sharedInformers.Ark().V1().Backups(),
|
|
|
|
client.ArkV1(),
|
2017-08-29 20:28:51 +00:00
|
|
|
sharedInformers.Ark().V1().Restores(),
|
|
|
|
client.ArkV1(),
|
2017-08-02 17:27:17 +00:00
|
|
|
).(*gcController)
|
|
|
|
controller.clock = fakeClock
|
|
|
|
|
2017-09-06 20:58:43 +00:00
|
|
|
backupService.On("GetAllBackups", bucket).Return(test.backups, nil)
|
|
|
|
for _, b := range test.expectedDeletions.List() {
|
|
|
|
backupService.On("DeleteBackupDir", bucket, b).Return(nil)
|
2017-08-02 17:27:17 +00:00
|
|
|
}
|
|
|
|
|
2017-09-06 20:58:43 +00:00
|
|
|
controller.processBackups()
|
|
|
|
|
2017-08-09 22:52:27 +00:00
|
|
|
if !test.nilSnapshotService {
|
|
|
|
assert.Equal(t, test.expectedSnapshotsRemaining, snapshotService.SnapshotsTaken)
|
|
|
|
}
|
2017-09-06 20:58:43 +00:00
|
|
|
|
|
|
|
backupService.AssertExpectations(t)
|
2017-08-02 17:27:17 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-29 20:28:51 +00:00
|
|
|
func TestGarbageCollectBackup(t *testing.T) {
|
|
|
|
tests := []struct {
|
2017-09-06 20:58:43 +00:00
|
|
|
name string
|
|
|
|
backup *api.Backup
|
|
|
|
deleteBackupFile bool
|
|
|
|
snapshots sets.String
|
|
|
|
backupFiles sets.String
|
|
|
|
backupMetadataFiles sets.String
|
|
|
|
restores []*api.Restore
|
|
|
|
expectedRestoreDeletes []string
|
|
|
|
expectedBackupDelete string
|
|
|
|
expectedSnapshots sets.String
|
|
|
|
expectedObjectStorageDeletions sets.String
|
2017-08-29 20:28:51 +00:00
|
|
|
}{
|
|
|
|
{
|
2017-09-06 20:58:43 +00:00
|
|
|
name: "deleteBackupFile=false, snapshot deletion fails, don't delete kube backup",
|
2017-08-29 20:28:51 +00:00
|
|
|
backup: NewTestBackup().WithName("backup-1").
|
|
|
|
WithSnapshot("pv-1", "snapshot-1").
|
|
|
|
WithSnapshot("pv-2", "snapshot-2").
|
|
|
|
Backup,
|
2017-09-06 20:58:43 +00:00
|
|
|
deleteBackupFile: false,
|
|
|
|
snapshots: sets.NewString("snapshot-1"),
|
|
|
|
expectedSnapshots: sets.NewString(),
|
|
|
|
expectedObjectStorageDeletions: sets.NewString(),
|
2017-08-29 20:28:51 +00:00
|
|
|
},
|
|
|
|
{
|
2017-09-06 20:58:43 +00:00
|
|
|
name: "related restores should be deleted",
|
|
|
|
backup: NewTestBackup().WithName("backup-1").Backup,
|
|
|
|
deleteBackupFile: true,
|
|
|
|
snapshots: sets.NewString(),
|
2017-08-29 20:28:51 +00:00
|
|
|
restores: []*api.Restore{
|
|
|
|
NewTestRestore(api.DefaultNamespace, "restore-1", api.RestorePhaseCompleted).WithBackup("backup-1").Restore,
|
|
|
|
NewTestRestore(api.DefaultNamespace, "restore-2", api.RestorePhaseCompleted).WithBackup("backup-2").Restore,
|
|
|
|
},
|
2017-09-06 20:58:43 +00:00
|
|
|
expectedRestoreDeletes: []string{"restore-1"},
|
|
|
|
expectedBackupDelete: "backup-1",
|
|
|
|
expectedSnapshots: sets.NewString(),
|
|
|
|
expectedObjectStorageDeletions: sets.NewString("backup-1"),
|
2017-08-29 20:28:51 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
var (
|
2017-09-06 20:58:43 +00:00
|
|
|
backupService = &BackupService{}
|
2017-08-29 20:28:51 +00:00
|
|
|
snapshotService = &FakeSnapshotService{SnapshotsTaken: test.snapshots}
|
|
|
|
client = fake.NewSimpleClientset()
|
|
|
|
sharedInformers = informers.NewSharedInformerFactory(client, 0)
|
2017-09-06 20:58:43 +00:00
|
|
|
bucket = "bucket-1"
|
2017-08-29 20:28:51 +00:00
|
|
|
controller = NewGCController(
|
|
|
|
backupService,
|
|
|
|
snapshotService,
|
2017-09-06 20:58:43 +00:00
|
|
|
bucket,
|
2017-08-29 20:28:51 +00:00
|
|
|
1*time.Millisecond,
|
|
|
|
sharedInformers.Ark().V1().Backups(),
|
|
|
|
client.ArkV1(),
|
|
|
|
sharedInformers.Ark().V1().Restores(),
|
|
|
|
client.ArkV1(),
|
|
|
|
).(*gcController)
|
|
|
|
)
|
|
|
|
|
|
|
|
sharedInformers.Ark().V1().Backups().Informer().GetStore().Add(test.backup)
|
|
|
|
for _, restore := range test.restores {
|
|
|
|
sharedInformers.Ark().V1().Restores().Informer().GetStore().Add(restore)
|
|
|
|
}
|
|
|
|
|
2017-09-06 20:58:43 +00:00
|
|
|
for _, b := range test.expectedObjectStorageDeletions.List() {
|
|
|
|
backupService.On("DeleteBackupDir", bucket, b).Return(nil)
|
|
|
|
}
|
|
|
|
|
2017-08-29 20:28:51 +00:00
|
|
|
// METHOD UNDER TEST
|
|
|
|
controller.garbageCollectBackup(test.backup, test.deleteBackupFile)
|
|
|
|
|
|
|
|
// VERIFY:
|
|
|
|
|
|
|
|
// remaining snapshots
|
|
|
|
assert.Equal(t, test.expectedSnapshots, snapshotService.SnapshotsTaken)
|
|
|
|
|
|
|
|
expectedActions := make([]core.Action, 0)
|
|
|
|
// Restore client deletes
|
|
|
|
for _, restore := range test.expectedRestoreDeletes {
|
|
|
|
action := core.NewDeleteAction(
|
|
|
|
api.SchemeGroupVersion.WithResource("restores"),
|
|
|
|
api.DefaultNamespace,
|
|
|
|
restore,
|
|
|
|
)
|
|
|
|
expectedActions = append(expectedActions, action)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Backup client deletes
|
|
|
|
if test.expectedBackupDelete != "" {
|
|
|
|
action := core.NewDeleteAction(
|
|
|
|
api.SchemeGroupVersion.WithResource("backups"),
|
|
|
|
api.DefaultNamespace,
|
|
|
|
test.expectedBackupDelete,
|
|
|
|
)
|
|
|
|
expectedActions = append(expectedActions, action)
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.Equal(t, expectedActions, client.Actions())
|
2017-09-06 20:58:43 +00:00
|
|
|
|
|
|
|
backupService.AssertExpectations(t)
|
2017-08-29 20:28:51 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-02 17:27:17 +00:00
|
|
|
func TestGarbageCollectPicksUpBackupUponExpiration(t *testing.T) {
|
|
|
|
var (
|
2017-09-06 20:58:43 +00:00
|
|
|
backupService = &BackupService{}
|
2017-08-02 17:27:17 +00:00
|
|
|
snapshotService = &FakeSnapshotService{}
|
|
|
|
fakeClock = clock.NewFakeClock(time.Now())
|
|
|
|
assert = assert.New(t)
|
|
|
|
)
|
|
|
|
|
|
|
|
scenario := gcTest{
|
2017-09-06 20:58:43 +00:00
|
|
|
name: "basic-expired",
|
|
|
|
backups: []*api.Backup{
|
|
|
|
NewTestBackup().WithName("backup-1").
|
|
|
|
WithExpiration(fakeClock.Now().Add(1*time.Second)).
|
|
|
|
WithSnapshot("pv-1", "snapshot-1").
|
|
|
|
WithSnapshot("pv-2", "snapshot-2").
|
|
|
|
Backup,
|
2017-08-02 17:27:17 +00:00
|
|
|
},
|
|
|
|
snapshots: sets.NewString("snapshot-1", "snapshot-2"),
|
|
|
|
}
|
|
|
|
|
|
|
|
snapshotService.SnapshotsTaken = scenario.snapshots
|
|
|
|
|
|
|
|
var (
|
|
|
|
client = fake.NewSimpleClientset()
|
|
|
|
sharedInformers = informers.NewSharedInformerFactory(client, 0)
|
|
|
|
)
|
|
|
|
|
|
|
|
controller := NewGCController(
|
|
|
|
backupService,
|
|
|
|
snapshotService,
|
2017-09-06 20:58:43 +00:00
|
|
|
"bucket",
|
2017-08-02 17:27:17 +00:00
|
|
|
1*time.Millisecond,
|
|
|
|
sharedInformers.Ark().V1().Backups(),
|
|
|
|
client.ArkV1(),
|
2017-08-29 20:28:51 +00:00
|
|
|
sharedInformers.Ark().V1().Restores(),
|
|
|
|
client.ArkV1(),
|
2017-08-02 17:27:17 +00:00
|
|
|
).(*gcController)
|
|
|
|
controller.clock = fakeClock
|
|
|
|
|
2017-09-06 20:58:43 +00:00
|
|
|
backupService.On("GetAllBackups", "bucket").Return(scenario.backups, nil)
|
|
|
|
|
2017-08-02 17:27:17 +00:00
|
|
|
// PASS 1
|
2017-08-29 20:28:51 +00:00
|
|
|
controller.processBackups()
|
2017-08-02 17:27:17 +00:00
|
|
|
|
2017-09-06 20:58:43 +00:00
|
|
|
backupService.AssertExpectations(t)
|
2017-08-02 17:27:17 +00:00
|
|
|
assert.Equal(scenario.snapshots, snapshotService.SnapshotsTaken, "snapshots should not be garbage-collected yet.")
|
|
|
|
|
|
|
|
// PASS 2
|
|
|
|
fakeClock.Step(1 * time.Minute)
|
2017-09-06 20:58:43 +00:00
|
|
|
backupService.On("DeleteBackupDir", "bucket", "backup-1").Return(nil)
|
2017-08-29 20:28:51 +00:00
|
|
|
controller.processBackups()
|
2017-08-02 17:27:17 +00:00
|
|
|
|
|
|
|
assert.Equal(0, len(snapshotService.SnapshotsTaken), "snapshots should have been garbage-collected.")
|
2017-08-14 14:14:30 +00:00
|
|
|
|
2017-09-06 20:58:43 +00:00
|
|
|
backupService.AssertExpectations(t)
|
2017-08-14 14:14:30 +00:00
|
|
|
}
|