2017-08-02 17:27:17 +00:00
|
|
|
/*
|
2018-01-02 18:51:49 +00:00
|
|
|
Copyright 2017 the Heptio Ark 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 (
|
2017-12-15 00:40:02 +00:00
|
|
|
"errors"
|
2017-08-02 17:27:17 +00:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
2017-12-15 00:40:02 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2017-08-02 17:27:17 +00:00
|
|
|
|
|
|
|
"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-10-25 16:42:03 +00:00
|
|
|
"github.com/heptio/ark/pkg/generated/clientset/versioned/fake"
|
2017-08-02 17:27:17 +00:00
|
|
|
informers "github.com/heptio/ark/pkg/generated/informers/externalversions"
|
2017-12-12 23:22:46 +00:00
|
|
|
arktest "github.com/heptio/ark/pkg/util/test"
|
2017-08-02 17:27:17 +00:00
|
|
|
)
|
|
|
|
|
2017-12-15 00:40:02 +00:00
|
|
|
func TestGCControllerRun(t *testing.T) {
|
2017-08-02 17:27:17 +00:00
|
|
|
fakeClock := clock.NewFakeClock(time.Now())
|
|
|
|
|
2017-12-15 00:40:02 +00:00
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
backups []*api.Backup
|
|
|
|
snapshots sets.String
|
|
|
|
expectedDeletions sets.String
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "no backups results in no deletions",
|
|
|
|
},
|
2017-10-26 21:22:39 +00:00
|
|
|
{
|
2017-12-15 00:40:02 +00:00
|
|
|
name: "expired backup is deleted",
|
2017-09-06 20:58:43 +00:00
|
|
|
backups: []*api.Backup{
|
2017-12-12 23:22:46 +00:00
|
|
|
arktest.NewTestBackup().WithName("backup-1").
|
2017-09-06 20:58:43 +00:00
|
|
|
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
|
|
|
},
|
2017-12-15 00:40:02 +00:00
|
|
|
expectedDeletions: sets.NewString("backup-1"),
|
2017-08-02 17:27:17 +00:00
|
|
|
},
|
2017-10-26 21:22:39 +00:00
|
|
|
{
|
2017-12-15 00:40:02 +00:00
|
|
|
name: "unexpired backup is not deleted",
|
2017-09-06 20:58:43 +00:00
|
|
|
backups: []*api.Backup{
|
2017-12-12 23:22:46 +00:00
|
|
|
arktest.NewTestBackup().WithName("backup-1").
|
2017-09-06 20:58:43 +00:00
|
|
|
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-12-15 00:40:02 +00:00
|
|
|
expectedDeletions: sets.NewString(),
|
2017-08-02 17:27:17 +00:00
|
|
|
},
|
2017-10-26 21:22:39 +00:00
|
|
|
{
|
2017-12-15 00:40:02 +00:00
|
|
|
name: "expired backup is deleted and unexpired backup is not deleted",
|
2017-09-06 20:58:43 +00:00
|
|
|
backups: []*api.Backup{
|
2017-12-12 23:22:46 +00:00
|
|
|
arktest.NewTestBackup().WithName("backup-1").
|
2017-09-06 20:58:43 +00:00
|
|
|
WithExpiration(fakeClock.Now().Add(-1*time.Minute)).
|
|
|
|
WithSnapshot("pv-1", "snapshot-1").
|
|
|
|
WithSnapshot("pv-2", "snapshot-2").
|
|
|
|
Backup,
|
2017-12-12 23:22:46 +00:00
|
|
|
arktest.NewTestBackup().WithName("backup-2").
|
2017-09-06 20:58:43 +00:00
|
|
|
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-12-15 00:40:02 +00:00
|
|
|
expectedDeletions: sets.NewString("backup-1"),
|
2017-08-09 22:52:27 +00:00
|
|
|
},
|
2017-08-02 17:27:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
var (
|
|
|
|
client = fake.NewSimpleClientset()
|
|
|
|
sharedInformers = informers.NewSharedInformerFactory(client, 0)
|
|
|
|
)
|
|
|
|
|
|
|
|
controller := NewGCController(
|
2017-12-15 00:40:02 +00:00
|
|
|
nil,
|
|
|
|
nil,
|
|
|
|
"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-12-15 00:40:02 +00:00
|
|
|
arktest.NewLogger(),
|
2017-08-02 17:27:17 +00:00
|
|
|
).(*gcController)
|
|
|
|
controller.clock = fakeClock
|
|
|
|
|
2017-12-15 00:40:02 +00:00
|
|
|
for _, backup := range test.backups {
|
|
|
|
sharedInformers.Ark().V1().Backups().Informer().GetStore().Add(backup)
|
2017-08-02 17:27:17 +00:00
|
|
|
}
|
|
|
|
|
2017-12-15 00:40:02 +00:00
|
|
|
expectedDeletions := make([]core.Action, 0, len(test.expectedDeletions))
|
|
|
|
for backup := range test.expectedDeletions {
|
|
|
|
expectedDeletions = append(expectedDeletions, core.NewDeleteAction(
|
|
|
|
api.SchemeGroupVersion.WithResource("backups"),
|
|
|
|
api.DefaultNamespace,
|
|
|
|
backup,
|
|
|
|
))
|
2017-08-09 22:52:27 +00:00
|
|
|
}
|
2017-09-06 20:58:43 +00:00
|
|
|
|
2017-12-15 00:40:02 +00:00
|
|
|
controller.run()
|
|
|
|
|
|
|
|
assert.Equal(t, expectedDeletions, client.Actions())
|
2017-08-02 17:27:17 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-29 20:28:51 +00:00
|
|
|
func TestGarbageCollectBackup(t *testing.T) {
|
|
|
|
tests := []struct {
|
2017-12-15 00:40:02 +00:00
|
|
|
name string
|
|
|
|
backup *api.Backup
|
|
|
|
snapshots sets.String
|
|
|
|
restores []*api.Restore
|
|
|
|
nilSnapshotService bool
|
|
|
|
expectErr bool
|
|
|
|
expectBackupDirDeleted bool
|
2017-08-29 20:28:51 +00:00
|
|
|
}{
|
|
|
|
{
|
2017-12-15 00:40:02 +00:00
|
|
|
name: "nil snapshot service when backup has snapshots returns error",
|
|
|
|
backup: arktest.NewTestBackup().WithName("backup-1").WithSnapshot("pv-1", "snap-1").Backup,
|
|
|
|
nilSnapshotService: true,
|
|
|
|
expectErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "nil snapshot service when backup doesn't have snapshots correctly garbage-collects",
|
|
|
|
backup: arktest.NewTestBackup().WithName("backup-1").Backup,
|
|
|
|
nilSnapshotService: true,
|
|
|
|
expectBackupDirDeleted: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "return error if snapshot deletion fails",
|
2017-12-12 23:22:46 +00:00
|
|
|
backup: arktest.NewTestBackup().WithName("backup-1").
|
2017-08-29 20:28:51 +00:00
|
|
|
WithSnapshot("pv-1", "snapshot-1").
|
|
|
|
WithSnapshot("pv-2", "snapshot-2").
|
|
|
|
Backup,
|
2017-12-15 00:40:02 +00:00
|
|
|
snapshots: sets.NewString("snapshot-1"),
|
|
|
|
expectBackupDirDeleted: true,
|
|
|
|
expectErr: true,
|
2017-08-29 20:28:51 +00:00
|
|
|
},
|
|
|
|
{
|
2017-12-15 00:40:02 +00:00
|
|
|
name: "related restores should be deleted",
|
|
|
|
backup: arktest.NewTestBackup().WithName("backup-1").Backup,
|
|
|
|
snapshots: sets.NewString(),
|
2017-08-29 20:28:51 +00:00
|
|
|
restores: []*api.Restore{
|
2017-12-12 23:22:46 +00:00
|
|
|
arktest.NewTestRestore(api.DefaultNamespace, "restore-1", api.RestorePhaseCompleted).WithBackup("backup-1").Restore,
|
|
|
|
arktest.NewTestRestore(api.DefaultNamespace, "restore-2", api.RestorePhaseCompleted).WithBackup("backup-2").Restore,
|
2017-08-29 20:28:51 +00:00
|
|
|
},
|
2017-12-15 00:40:02 +00:00
|
|
|
expectBackupDirDeleted: true,
|
2017-08-29 20:28:51 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
var (
|
2017-12-12 23:22:46 +00:00
|
|
|
backupService = &arktest.BackupService{}
|
|
|
|
snapshotService = &arktest.FakeSnapshotService{SnapshotsTaken: test.snapshots}
|
2017-08-29 20:28:51 +00:00
|
|
|
client = fake.NewSimpleClientset()
|
|
|
|
sharedInformers = informers.NewSharedInformerFactory(client, 0)
|
|
|
|
controller = NewGCController(
|
|
|
|
backupService,
|
|
|
|
snapshotService,
|
2017-12-15 00:40:02 +00:00
|
|
|
"bucket-1",
|
2017-08-29 20:28:51 +00:00
|
|
|
1*time.Millisecond,
|
|
|
|
sharedInformers.Ark().V1().Backups(),
|
|
|
|
client.ArkV1(),
|
|
|
|
sharedInformers.Ark().V1().Restores(),
|
|
|
|
client.ArkV1(),
|
2017-12-15 00:40:02 +00:00
|
|
|
arktest.NewLogger(),
|
2017-08-29 20:28:51 +00:00
|
|
|
).(*gcController)
|
|
|
|
)
|
|
|
|
|
2017-12-15 00:40:02 +00:00
|
|
|
if test.nilSnapshotService {
|
|
|
|
controller.snapshotService = nil
|
|
|
|
}
|
|
|
|
|
2017-08-29 20:28:51 +00:00
|
|
|
sharedInformers.Ark().V1().Backups().Informer().GetStore().Add(test.backup)
|
|
|
|
for _, restore := range test.restores {
|
|
|
|
sharedInformers.Ark().V1().Restores().Informer().GetStore().Add(restore)
|
|
|
|
}
|
|
|
|
|
2017-12-15 00:40:02 +00:00
|
|
|
if test.expectBackupDirDeleted {
|
|
|
|
backupService.On("DeleteBackupDir", controller.bucket, test.backup.Name).Return(nil)
|
2017-09-06 20:58:43 +00:00
|
|
|
}
|
|
|
|
|
2017-08-29 20:28:51 +00:00
|
|
|
// METHOD UNDER TEST
|
2017-12-15 00:40:02 +00:00
|
|
|
err := controller.garbageCollect(test.backup, controller.logger)
|
2017-08-29 20:28:51 +00:00
|
|
|
|
|
|
|
// VERIFY:
|
|
|
|
|
2017-12-15 00:40:02 +00:00
|
|
|
// error
|
|
|
|
assert.Equal(t, test.expectErr, err != nil)
|
|
|
|
|
2017-08-29 20:28:51 +00:00
|
|
|
// remaining snapshots
|
2017-12-15 00:40:02 +00:00
|
|
|
if !test.nilSnapshotService {
|
|
|
|
backupSnapshots := sets.NewString()
|
|
|
|
for _, snapshot := range test.backup.Status.VolumeBackups {
|
|
|
|
backupSnapshots.Insert(snapshot.SnapshotID)
|
|
|
|
}
|
2017-08-29 20:28:51 +00:00
|
|
|
|
2017-12-15 00:40:02 +00:00
|
|
|
assert.Equal(t, test.snapshots.Difference(backupSnapshots), snapshotService.SnapshotsTaken)
|
2017-08-29 20:28:51 +00:00
|
|
|
}
|
|
|
|
|
2017-12-15 00:40:02 +00:00
|
|
|
// restore client deletes
|
|
|
|
expectedActions := make([]core.Action, 0)
|
|
|
|
for _, restore := range test.restores {
|
|
|
|
if restore.Spec.BackupName != test.backup.Name {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2017-08-29 20:28:51 +00:00
|
|
|
action := core.NewDeleteAction(
|
2017-12-15 00:40:02 +00:00
|
|
|
api.SchemeGroupVersion.WithResource("restores"),
|
2017-08-29 20:28:51 +00:00
|
|
|
api.DefaultNamespace,
|
2017-12-15 00:40:02 +00:00
|
|
|
restore.Name,
|
2017-08-29 20:28:51 +00:00
|
|
|
)
|
|
|
|
expectedActions = append(expectedActions, action)
|
|
|
|
}
|
|
|
|
assert.Equal(t, expectedActions, client.Actions())
|
2017-09-06 20:58:43 +00:00
|
|
|
|
2017-12-15 00:40:02 +00:00
|
|
|
// backup dir deletion
|
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 (
|
|
|
|
fakeClock = clock.NewFakeClock(time.Now())
|
2017-12-15 00:40:02 +00:00
|
|
|
client = fake.NewSimpleClientset()
|
|
|
|
sharedInformers = informers.NewSharedInformerFactory(client, 0)
|
|
|
|
backup = arktest.NewTestBackup().WithName("backup-1").
|
2017-09-06 20:58:43 +00:00
|
|
|
WithExpiration(fakeClock.Now().Add(1*time.Second)).
|
|
|
|
WithSnapshot("pv-1", "snapshot-1").
|
|
|
|
WithSnapshot("pv-2", "snapshot-2").
|
2017-12-15 00:40:02 +00:00
|
|
|
Backup
|
2017-08-02 17:27:17 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
controller := NewGCController(
|
2017-12-15 00:40:02 +00:00
|
|
|
nil,
|
|
|
|
nil,
|
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-12-15 00:40:02 +00:00
|
|
|
arktest.NewLogger(),
|
2017-08-02 17:27:17 +00:00
|
|
|
).(*gcController)
|
|
|
|
controller.clock = fakeClock
|
|
|
|
|
2017-12-15 00:40:02 +00:00
|
|
|
sharedInformers.Ark().V1().Backups().Informer().GetStore().Add(backup)
|
2017-09-06 20:58:43 +00:00
|
|
|
|
2017-08-02 17:27:17 +00:00
|
|
|
// PASS 1
|
2017-12-15 00:40:02 +00:00
|
|
|
controller.run()
|
|
|
|
assert.Equal(t, 0, len(client.Actions()))
|
2017-08-02 17:27:17 +00:00
|
|
|
|
|
|
|
// PASS 2
|
2017-12-15 00:40:02 +00:00
|
|
|
expectedActions := []core.Action{
|
|
|
|
core.NewDeleteAction(
|
|
|
|
api.SchemeGroupVersion.WithResource("backups"),
|
|
|
|
api.DefaultNamespace,
|
|
|
|
"backup-1",
|
|
|
|
),
|
|
|
|
}
|
|
|
|
|
2017-08-02 17:27:17 +00:00
|
|
|
fakeClock.Step(1 * time.Minute)
|
2017-12-15 00:40:02 +00:00
|
|
|
controller.run()
|
2017-08-02 17:27:17 +00:00
|
|
|
|
2017-12-15 00:40:02 +00:00
|
|
|
assert.Equal(t, expectedActions, client.Actions())
|
|
|
|
}
|
2017-08-14 14:14:30 +00:00
|
|
|
|
2017-12-15 00:40:02 +00:00
|
|
|
func TestHandleFinalizer(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
backup *api.Backup
|
|
|
|
deleteBackupDirError bool
|
|
|
|
expectGarbageCollect bool
|
|
|
|
expectedPatch []byte
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "nil deletionTimestamp exits early",
|
|
|
|
backup: arktest.NewTestBackup().Backup,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "no finalizers exits early",
|
|
|
|
backup: arktest.NewTestBackup().WithDeletionTimestamp(time.Now()).Backup,
|
|
|
|
},
|
|
|
|
{
|
2018-02-12 16:32:49 +00:00
|
|
|
name: "no GCFinalizer exits early",
|
2017-12-15 00:40:02 +00:00
|
|
|
backup: arktest.NewTestBackup().WithDeletionTimestamp(time.Now()).WithFinalizers("foo").Backup,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "error when calling garbageCollect exits without patch",
|
2018-02-12 16:32:49 +00:00
|
|
|
backup: arktest.NewTestBackup().WithDeletionTimestamp(time.Now()).WithFinalizers(api.GCFinalizer).Backup,
|
2017-12-15 00:40:02 +00:00
|
|
|
deleteBackupDirError: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "normal case - patch includes the appropriate fields",
|
2018-02-12 16:32:49 +00:00
|
|
|
backup: arktest.NewTestBackup().WithDeletionTimestamp(time.Now()).WithFinalizers(api.GCFinalizer, "foo").WithResourceVersion("1").Backup,
|
2017-12-15 00:40:02 +00:00
|
|
|
expectGarbageCollect: true,
|
|
|
|
expectedPatch: []byte(`{"metadata":{"finalizers":["foo"],"resourceVersion":"1"}}`),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
var (
|
|
|
|
backupService = &arktest.BackupService{}
|
|
|
|
client = fake.NewSimpleClientset()
|
|
|
|
sharedInformers = informers.NewSharedInformerFactory(client, 0)
|
|
|
|
controller = NewGCController(
|
|
|
|
backupService,
|
|
|
|
nil,
|
|
|
|
"bucket-1",
|
|
|
|
1*time.Millisecond,
|
|
|
|
sharedInformers.Ark().V1().Backups(),
|
|
|
|
client.ArkV1(),
|
|
|
|
sharedInformers.Ark().V1().Restores(),
|
|
|
|
client.ArkV1(),
|
|
|
|
arktest.NewLogger(),
|
|
|
|
).(*gcController)
|
|
|
|
)
|
|
|
|
|
|
|
|
if test.expectGarbageCollect {
|
|
|
|
backupService.On("DeleteBackupDir", controller.bucket, test.backup.Name).Return(nil)
|
|
|
|
} else if test.deleteBackupDirError {
|
|
|
|
backupService.On("DeleteBackupDir", controller.bucket, test.backup.Name).Return(errors.New("foo"))
|
|
|
|
}
|
|
|
|
|
|
|
|
// METHOD UNDER TEST
|
2018-03-01 00:56:23 +00:00
|
|
|
controller.handleFinalizer(test.backup)
|
2017-12-15 00:40:02 +00:00
|
|
|
|
|
|
|
// VERIFY
|
|
|
|
backupService.AssertExpectations(t)
|
|
|
|
|
|
|
|
actions := client.Actions()
|
|
|
|
|
|
|
|
if test.expectedPatch == nil {
|
|
|
|
assert.Equal(t, 0, len(actions))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
require.Equal(t, 1, len(actions))
|
|
|
|
patchAction, ok := actions[0].(core.PatchAction)
|
|
|
|
require.True(t, ok, "action is not a PatchAction")
|
|
|
|
|
|
|
|
assert.Equal(t, test.expectedPatch, patchAction.GetPatch())
|
|
|
|
})
|
|
|
|
}
|
2017-08-14 14:14:30 +00:00
|
|
|
}
|