Skip to content

Commit

Permalink
Allow specifying VM RunStrategy before restore
Browse files Browse the repository at this point in the history
This commit implements a new label that allows switching the VM RunStrategy before restore.

Signed-off-by: Alvaro Romero <[email protected]>
  • Loading branch information
alromeros committed Dec 17, 2024
1 parent 8d5aefb commit 8e66c89
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 18 deletions.
10 changes: 2 additions & 8 deletions pkg/plugin/vm_backup_item_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,6 @@ type VMBackupItemAction struct {
log logrus.FieldLogger
}

const (
// MetadataBackupLabel indicates that the object will be backed up for metadata purposes.
// This allows skipping restore and consistency-specific checks while ensuring the object is backed up.
MetadataBackupLabel = "velero.kubevirt.io/metadataBackup"
)

// NewVMBackupItemAction instantiates a VMBackupItemAction.
func NewVMBackupItemAction(log logrus.FieldLogger) *VMBackupItemAction {
return &VMBackupItemAction{log: log}
Expand Down Expand Up @@ -89,7 +83,7 @@ func (p *VMBackupItemAction) Execute(item runtime.Unstructured, backup *v1.Backu

// we can skip all checks that ensure consistency
// if we just want to backup for metadata purposes
if !metav1.HasLabel(backup.ObjectMeta, MetadataBackupLabel) {
if !util.IsMetadataBackup(backup) {
skipVolume := func(volume kvcore.Volume) bool {
return volumeInDVTemplates(volume, vm)
}
Expand Down Expand Up @@ -157,7 +151,7 @@ var isVMIExcludedByLabel = func(vm *kvcore.VirtualMachine) (bool, error) {
return false, nil
}

label, ok := labels[util.VELERO_EXCLUDE_LABEL]
label, ok := labels[util.VeleroExcludeLabel]
return ok && label == "true", nil
}

Expand Down
8 changes: 8 additions & 0 deletions pkg/plugin/vm_restore_item_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ import (
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/utils/ptr"
kvcore "kubevirt.io/api/core/v1"
"kubevirt.io/kubevirt-velero-plugin/pkg/util"
"kubevirt.io/kubevirt-velero-plugin/pkg/util/kvgraph"
)

Expand Down Expand Up @@ -63,6 +65,12 @@ func (p *VMRestorePlugin) Execute(input *velero.RestoreItemActionExecuteInput) (
return nil, errors.WithStack(err)
}

if runStrategy, ok := util.GetRestoreRunStrategy(input.Restore); ok {
p.log.Infof("Setting virtual machine run strategy to %s", runStrategy)
vm.Spec.RunStrategy = ptr.To(runStrategy)
vm.Spec.Running = nil
}

item, err := runtime.DefaultUnstructuredConverter.ToUnstructured(vm)
if err != nil {
return nil, errors.WithStack(err)
Expand Down
53 changes: 49 additions & 4 deletions pkg/plugin/vm_restore_item_action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import (

"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

Expand All @@ -19,7 +21,7 @@ func TestVmRestoreExecute(t *testing.T) {
"name": "test-vm",
},
"spec": map[string]interface{}{
"running": true,
"runStrategy": "Always",
"dataVolumeTemplates": []map[string]interface{}{
{"metadata": map[string]interface{}{
"name": "test-dv-1",
Expand Down Expand Up @@ -49,6 +51,15 @@ func TestVmRestoreExecute(t *testing.T) {
},
},
},
Restore: &velerov1.Restore{
ObjectMeta: metav1.ObjectMeta{
Name: "test-restore",
Namespace: "default",
},
Spec: velerov1.RestoreSpec{
IncludedNamespaces: []string{"default"},
},
},
}

logrus.SetLevel(logrus.InfoLevel)
Expand All @@ -58,17 +69,51 @@ func TestVmRestoreExecute(t *testing.T) {
assert.Nil(t, err)

spec := output.UpdatedItem.UnstructuredContent()["spec"].(map[string]interface{})
assert.Equal(t, true, spec["running"])
assert.Equal(t, "Always", spec["runStrategy"])
})

t.Run("Stopped VM should be restored stopped", func(t *testing.T) {
spec := input.Item.UnstructuredContent()["spec"].(map[string]interface{})
spec["running"] = false
spec["runStrategy"] = "Halted"
output, err := action.Execute(&input)
assert.Nil(t, err)

spec = output.UpdatedItem.UnstructuredContent()["spec"].(map[string]interface{})
assert.Equal(t, "Halted", spec["runStrategy"])
})

t.Run("Stopped VM should be restored running when using appropriate label", func(t *testing.T) {
spec := input.Item.UnstructuredContent()["spec"].(map[string]interface{})
spec["runStrategy"] = "Halted"
input.Restore.Labels = map[string]string{"velero.kubevirt.io/restore-run-strategy": "Always"}
output, err := action.Execute(&input)
assert.Nil(t, err)

spec = output.UpdatedItem.UnstructuredContent()["spec"].(map[string]interface{})
assert.Equal(t, "Always", spec["runStrategy"])
})

t.Run("Running VM should be restored stopped when using appropriate label", func(t *testing.T) {
spec := input.Item.UnstructuredContent()["spec"].(map[string]interface{})
spec["runStrategy"] = "Always"
input.Restore.Labels = map[string]string{"velero.kubevirt.io/restore-run-strategy": "Halted"}
output, err := action.Execute(&input)
assert.Nil(t, err)

spec = output.UpdatedItem.UnstructuredContent()["spec"].(map[string]interface{})
assert.Equal(t, "Halted", spec["runStrategy"])
})

t.Run("Running field should be cleared when run strategy annotation", func(t *testing.T) {
spec := input.Item.UnstructuredContent()["spec"].(map[string]interface{})
spec["running"] = "yes"
input.Restore.Labels = map[string]string{"velero.kubevirt.io/restore-run-strategy": "Halted"}
output, err := action.Execute(&input)
assert.Nil(t, err)

spec = output.UpdatedItem.UnstructuredContent()["spec"].(map[string]interface{})
assert.Equal(t, false, spec["running"])
assert.Equal(t, "Halted", spec["runStrategy"])
assert.Equal(t, nil, spec["running"])
})

t.Run("VM should return DVs as additional items", func(t *testing.T) {
Expand Down
6 changes: 3 additions & 3 deletions pkg/plugin/vmi_backup_item_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (p *VMIBackupItemAction) Execute(item runtime.Unstructured, backup *v1.Back

if isVMIOwned(vmi) {
util.AddAnnotation(item, AnnIsOwned, "true")
} else if !metav1.HasLabel(backup.ObjectMeta, MetadataBackupLabel) {
} else if !util.IsMetadataBackup(backup) {
restore, err := util.RestorePossible(vmi.Spec.Volumes, backup, vmi.Namespace, func(volume kvcore.Volume) bool { return false }, p.log)
if err != nil {
return nil, nil, errors.WithStack(err)
Expand Down Expand Up @@ -156,7 +156,7 @@ var isVMExcludedByLabel = func(vmi *kvcore.VirtualMachineInstance) (bool, error)
return false, err
}

label, ok := vm.GetLabels()[util.VELERO_EXCLUDE_LABEL]
label, ok := vm.GetLabels()[util.VeleroExcludeLabel]
return ok && label == "true", nil
}

Expand All @@ -174,6 +174,6 @@ func (p *VMIBackupItemAction) isPodExcludedByLabel(vmi *kvcore.VirtualMachineIns
return false, nil
}

label, ok := labels[util.VELERO_EXCLUDE_LABEL]
label, ok := labels[util.VeleroExcludeLabel]
return ok && label == "true", nil
}
27 changes: 24 additions & 3 deletions pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,17 @@ import (
cdiv1 "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1"
)

const VELERO_EXCLUDE_LABEL = "velero.io/exclude-from-backup"
const (
// MetadataBackupLabel indicates that the object will be backed up for metadata purposes.
// This allows skipping restore and consistency-specific checks while ensuring the object is backed up.
MetadataBackupLabel = "velero.kubevirt.io/metadataBackup"

// RestoreRunStrategy indicates that the backed up VMs will be powered with the specified run strategy after restore.
RestoreRunStrategy = "velero.kubevirt.io/restore-run-strategy"

// VeleroExcludeLabel is used to exclude an object from Velero backups.
VeleroExcludeLabel = "velero.io/exclude-from-backup"
)

func GetK8sClient() (*kubernetes.Clientset, error) {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
Expand Down Expand Up @@ -209,7 +219,7 @@ var IsDVExcludedByLabel = func(namespace, dvName string) (bool, error) {
return false, nil
}

label, ok := labels[VELERO_EXCLUDE_LABEL]
label, ok := labels[VeleroExcludeLabel]
return ok && label == "true", nil
}

Expand All @@ -230,7 +240,7 @@ var IsPVCExcludedByLabel = func(namespace, pvcName string) (bool, error) {
return false, nil
}

label, ok := labels[VELERO_EXCLUDE_LABEL]
label, ok := labels[VeleroExcludeLabel]
return ok && label == "true", nil
}

Expand Down Expand Up @@ -307,3 +317,14 @@ func getNamespaceAndNetworkName(vmiNamespace, fullNetworkName string) (string, s
}
return vmiNamespace, fullNetworkName
}

func GetRestoreRunStrategy(restore *velerov1.Restore) (kvv1.VirtualMachineRunStrategy, bool) {
if metav1.HasLabel(restore.ObjectMeta, RestoreRunStrategy) {
return kvv1.VirtualMachineRunStrategy(restore.Labels[RestoreRunStrategy]), true
}
return "", false
}

func IsMetadataBackup(backup *velerov1.Backup) bool {
return metav1.HasLabel(backup.ObjectMeta, MetadataBackupLabel)
}

0 comments on commit 8e66c89

Please sign in to comment.