mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-01-08 14:21:18 +00:00
Allow forced backup deletion
Add --force and --confirm to `ark backup delete` to support forced backup deletion. This forcibly removes the Ark GC finalizer (if it's present) from a backup and will orphan any resources associated with the backup, such as backup tarballs in object storage, persistent volume snapshots, and restores for the backup. If a backup has a deletion timestamp, display `Deleting` in `ark backup describe` and `ark backup get`. Signed-off-by: Andy Goldstein <andy.goldstein@gmail.com>
This commit is contained in:
@@ -49,6 +49,7 @@ import (
|
||||
"github.com/heptio/ark/pkg/util/collections"
|
||||
"github.com/heptio/ark/pkg/util/encode"
|
||||
kubeutil "github.com/heptio/ark/pkg/util/kube"
|
||||
"github.com/heptio/ark/pkg/util/stringslice"
|
||||
)
|
||||
|
||||
const backupVersion = 1
|
||||
@@ -237,8 +238,8 @@ func (controller *backupController) processBackup(key string) error {
|
||||
backup.Status.Version = backupVersion
|
||||
|
||||
// add GC finalizer if it's not there already
|
||||
if !has(backup.Finalizers, gcFinalizer) {
|
||||
backup.Finalizers = append(backup.Finalizers, gcFinalizer)
|
||||
if !stringslice.Has(backup.Finalizers, api.GCFinalizer) {
|
||||
backup.Finalizers = append(backup.Finalizers, api.GCFinalizer)
|
||||
}
|
||||
|
||||
// calculate expiration
|
||||
|
||||
@@ -190,7 +190,7 @@ func TestProcessBackup(t *testing.T) {
|
||||
backup.Status.Phase = v1.BackupPhaseInProgress
|
||||
backup.Status.Expiration.Time = expiration
|
||||
backup.Status.Version = 1
|
||||
backup.Finalizers = []string{gcFinalizer}
|
||||
backup.Finalizers = []string{v1.GCFinalizer}
|
||||
backupper.On("Backup", backup, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
cloudBackups.On("UploadBackup", "bucket", backup.Name, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
@@ -226,7 +226,7 @@ func TestProcessBackup(t *testing.T) {
|
||||
res.Status.Version = 1
|
||||
res.Status.Expiration.Time = expiration
|
||||
res.Status.Phase = v1.BackupPhase(phase)
|
||||
res.Finalizers = []string{gcFinalizer}
|
||||
res.Finalizers = []string{v1.GCFinalizer}
|
||||
|
||||
return true, res, nil
|
||||
})
|
||||
@@ -274,7 +274,7 @@ func TestProcessBackup(t *testing.T) {
|
||||
finalizers, err := collections.GetSlice(patch, "metadata.finalizers")
|
||||
require.NoError(t, err, "patch does not contain metadata.finalizers")
|
||||
assert.Equal(t, 1, len(finalizers))
|
||||
assert.Equal(t, gcFinalizer, finalizers[0])
|
||||
assert.Equal(t, v1.GCFinalizer, finalizers[0])
|
||||
|
||||
res, _ = collections.GetMap(patch, "metadata")
|
||||
assert.Equal(t, 1, len(res), "patch's metadata has the wrong number of keys")
|
||||
|
||||
@@ -39,10 +39,9 @@ import (
|
||||
informers "github.com/heptio/ark/pkg/generated/informers/externalversions/ark/v1"
|
||||
listers "github.com/heptio/ark/pkg/generated/listers/ark/v1"
|
||||
"github.com/heptio/ark/pkg/util/kube"
|
||||
"github.com/heptio/ark/pkg/util/stringslice"
|
||||
)
|
||||
|
||||
const gcFinalizer = "gc.ark.heptio.com"
|
||||
|
||||
// MinVersionForDelete is the minimum Kubernetes server version that Ark
|
||||
// requires in order to be able to properly delete backups (including
|
||||
// the associated snapshots and object storage files). This is because
|
||||
@@ -122,7 +121,7 @@ func (c *gcController) handleFinalizer(_, newObj interface{}) {
|
||||
}
|
||||
log.Debugf("Backup has finalizers %s", backup.Finalizers)
|
||||
|
||||
if !has(backup.Finalizers, gcFinalizer) {
|
||||
if !stringslice.Has(backup.Finalizers, api.GCFinalizer) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -137,7 +136,7 @@ func (c *gcController) handleFinalizer(_, newObj interface{}) {
|
||||
|
||||
patchMap := map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"finalizers": except(backup.Finalizers, gcFinalizer),
|
||||
"finalizers": stringslice.Except(backup.Finalizers, api.GCFinalizer),
|
||||
"resourceVersion": backup.ResourceVersion,
|
||||
},
|
||||
}
|
||||
@@ -153,32 +152,6 @@ func (c *gcController) handleFinalizer(_, newObj interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// has returns true if the `items` slice contains the
|
||||
// value `val`, or false otherwise.
|
||||
func has(items []string, val string) bool {
|
||||
for _, itm := range items {
|
||||
if itm == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// except returns a new string slice that contains all of the entries
|
||||
// from `items` except `val`.
|
||||
func except(items []string, val string) []string {
|
||||
var newItems []string
|
||||
|
||||
for _, itm := range items {
|
||||
if itm != val {
|
||||
newItems = append(newItems, itm)
|
||||
}
|
||||
}
|
||||
|
||||
return newItems
|
||||
}
|
||||
|
||||
// Run is a blocking function that runs a single worker to garbage-collect backups
|
||||
// from object/block storage and the Ark API. It will return when it receives on the
|
||||
// ctx.Done() channel.
|
||||
|
||||
@@ -306,17 +306,17 @@ func TestHandleFinalizer(t *testing.T) {
|
||||
backup: arktest.NewTestBackup().WithDeletionTimestamp(time.Now()).Backup,
|
||||
},
|
||||
{
|
||||
name: "no gcFinalizer exits early",
|
||||
name: "no GCFinalizer exits early",
|
||||
backup: arktest.NewTestBackup().WithDeletionTimestamp(time.Now()).WithFinalizers("foo").Backup,
|
||||
},
|
||||
{
|
||||
name: "error when calling garbageCollect exits without patch",
|
||||
backup: arktest.NewTestBackup().WithDeletionTimestamp(time.Now()).WithFinalizers(gcFinalizer).Backup,
|
||||
backup: arktest.NewTestBackup().WithDeletionTimestamp(time.Now()).WithFinalizers(api.GCFinalizer).Backup,
|
||||
deleteBackupDirError: true,
|
||||
},
|
||||
{
|
||||
name: "normal case - patch includes the appropriate fields",
|
||||
backup: arktest.NewTestBackup().WithDeletionTimestamp(time.Now()).WithFinalizers(gcFinalizer, "foo").WithResourceVersion("1").Backup,
|
||||
backup: arktest.NewTestBackup().WithDeletionTimestamp(time.Now()).WithFinalizers(api.GCFinalizer, "foo").WithResourceVersion("1").Backup,
|
||||
expectGarbageCollect: true,
|
||||
expectedPatch: []byte(`{"metadata":{"finalizers":["foo"],"resourceVersion":"1"}}`),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user