Skip to content

Commit

Permalink
[release-v0.7] Support VirtualMachineClusterInstancetype and VirtualM…
Browse files Browse the repository at this point in the history
…achineClusterPreference (#274)

* fix backup script

Signed-off-by: Michael Henriksen <[email protected]>

* handle clusterinstancetype and clusterpreference

Signed-off-by: Michael Henriksen <[email protected]>

---------

Signed-off-by: Michael Henriksen <[email protected]>
Co-authored-by: Michael Henriksen <[email protected]>
  • Loading branch information
kubevirt-bot and mhenriks authored Aug 27, 2024
1 parent 568b5c0 commit a8423da
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 61 deletions.
7 changes: 3 additions & 4 deletions cmd/velero-backup-restore/velero-backup-restore.sh
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,13 @@ create_backup() {
local backup_cmd="$VELERO_CLI create backup $backup_name --namespace $namespace --include-namespaces $include_ns --wait"

if [ -n "$selector" ]; then
backup_cmd+=("--selector" "$selector")
backup_cmd="$backup_cmd --selector $selector"
fi
if [ -n "$include_resources" ]; then
backup_cmd+=("--include-resources" "$include_resources")
backup_cmd="$backup_cmd --include-resources $include_resources"
fi
if [ -n "$snapshot_location" ]; then
backup_cmd+=("--volume-snapshot-locations" "$snapshot_location")
backup_cmd="$backup_cmd --volume-snapshot-locations $snapshot_location"
fi

# Execute backup command
Expand Down Expand Up @@ -257,4 +257,3 @@ case $command in
esac

echo "Exiting..."

29 changes: 25 additions & 4 deletions pkg/plugin/vm_backup_item_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package plugin
import (
"context"
"fmt"
"strings"

"github.com/pkg/errors"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -141,8 +142,18 @@ func (p *VMBackupItemAction) canBeSafelyBackedUp(vm *kvcore.VirtualMachine, back

func (p *VMBackupItemAction) addVMObjectGraph(vm *kvcore.VirtualMachine, extra []velero.ResourceIdentifier) []velero.ResourceIdentifier {
if vm.Spec.Instancetype != nil {
switch vm.Spec.Instancetype.Kind {
//TODO handle VirtualMachineClusterInstancetype
switch strings.ToLower(vm.Spec.Instancetype.Kind) {
case "virtualmachineclusterinstancetype":
p.log.Infof("Adding cluster instance type %s to the backup", vm.Spec.Instancetype.Name)
extra = append(extra, velero.ResourceIdentifier{
GroupResource: schema.GroupResource{Group: "instancetype.kubevirt.io", Resource: "virtualmachineclusterinstancetype"},
Name: vm.Spec.Instancetype.Name,
})
extra = append(extra, velero.ResourceIdentifier{
GroupResource: schema.GroupResource{Group: "apps", Resource: "controllerrevisions"},
Namespace: vm.Namespace,
Name: vm.Spec.Instancetype.RevisionName,
})
case "virtualmachineinstancetype":
p.log.Infof("Adding instance type %s to the backup", vm.Spec.Instancetype.Name)
extra = append(extra, velero.ResourceIdentifier{
Expand All @@ -159,8 +170,18 @@ func (p *VMBackupItemAction) addVMObjectGraph(vm *kvcore.VirtualMachine, extra [
}

if vm.Spec.Preference != nil {
//TODO handle VirtualMachineClusterPreference
switch vm.Spec.Preference.Kind {
switch strings.ToLower(vm.Spec.Preference.Kind) {
case "virtualmachineclusterpreference":
p.log.Infof("Adding cluster preference %s to the backup", vm.Spec.Preference.Name)
extra = append(extra, velero.ResourceIdentifier{
GroupResource: schema.GroupResource{Group: "instancetype.kubevirt.io", Resource: "virtualmachineclusterpreference"},
Name: vm.Spec.Preference.Name,
})
extra = append(extra, velero.ResourceIdentifier{
GroupResource: schema.GroupResource{Group: "apps", Resource: "controllerrevisions"},
Namespace: vm.Namespace,
Name: vm.Spec.Preference.RevisionName,
})
case "virtualmachinepreference":
p.log.Infof("Adding preference %s to the backup", vm.Spec.Preference.Name)
extra = append(extra, velero.ResourceIdentifier{
Expand Down
4 changes: 2 additions & 2 deletions tests/framework/kubectl.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/pkg/errors"
)

//RunKubectlCommand runs a kubectl Cmd and returns output and err
// RunKubectlCommand runs a kubectl Cmd and returns output and err
func (f *Framework) RunKubectlCommand(args ...string) error {
cmd := f.CreateKubectlCommand(args...)
outBytes, err := cmd.CombinedOutput()
Expand Down Expand Up @@ -84,7 +84,7 @@ func (f *Framework) KubectlDescribeVeleroBackup(ctx context.Context, podName, ba
if err != nil {
return result, err
}
json.Unmarshal(jsonBuf, &result)
err = json.Unmarshal(jsonBuf, &result)
if err != nil {
return result, err
}
Expand Down
15 changes: 15 additions & 0 deletions tests/framework/manifests_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,21 @@ func (f *Framework) CreateInstancetype() error {
return err
}

func (f *Framework) CreateClusterInstancetype() error {
err := f.RunKubectlCommand("create", "-f", "manifests/cluster-instancetype.yaml", "-n", f.Namespace.Name)
return err
}

func (f *Framework) CreatePreference() error {
err := f.RunKubectlCommand("create", "-f", "manifests/preference.yaml", "-n", f.Namespace.Name)
return err
}

func (f *Framework) CreateClusterPreference() error {
err := f.RunKubectlCommand("create", "-f", "manifests/cluster-preference.yaml", "-n", f.Namespace.Name)
return err
}

func (f *Framework) CreateConfigMap() error {
err := f.RunKubectlCommand("create", "-f", "manifests/configmap.yaml", "-n", f.Namespace.Name)
return err
Expand Down Expand Up @@ -45,6 +55,11 @@ func (f *Framework) CreateVMWithInstancetypeAndPreference() error {
return err
}

func (f *Framework) CreateVMWithClusterInstancetypeAndClusterPreference() error {
err := f.RunKubectlCreateYamlCommand("manifests/vm_with_clusterinstancetype_and_clusterpreference.yaml")
return err
}

func (f *Framework) CreateVMWithDifferentVolumes() error {
err := f.RunKubectlCreateYamlCommand("manifests/vm_with_different_volume_types.yaml")
return err
Expand Down
9 changes: 9 additions & 0 deletions tests/manifests/cluster-instancetype.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: instancetype.kubevirt.io/v1beta1
kind: VirtualMachineClusterInstancetype
metadata:
name: test-vm-instancetype
spec:
cpu:
guest: 1
memory:
guest: 256Mi
7 changes: 7 additions & 0 deletions tests/manifests/cluster-preference.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: instancetype.kubevirt.io/v1beta1
kind: VirtualMachineClusterPreference
metadata:
name: test-vm-preference
spec:
cpu:
preferredCPUTopology: "preferSockets"
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: test-vm-with-instancetype-and-preference
labels:
a.test.label: included
spec:
instancetype:
name: test-vm-instancetype
kind: VirtualMachineClusterInstancetype
preference:
name: test-vm-preference
kind: VirtualMachineClusterPreference
dataVolumeTemplates:
- metadata:
name: test-dv
spec:
pvc:
accessModes:
- ReadWriteOnce
volumeMode: Block
resources:
requests:
storage: 1Gi
storageClassName: {{KVP_STORAGE_CLASS}}
source:
registry:
pullMethod: node
url: docker://quay.io/kubevirt/alpine-with-test-tooling-container-disk:v0.57.1
running: true
template:
metadata:
creationTimestamp: null
name: test-vm-with-instancetype-and-preference
spec:
domain:
devices:
disks:
- disk:
bus: virtio
name: volume0
- disk:
bus: virtio
name: volume1
interfaces:
- masquerade: {}
name: default
rng: {}
machine:
type: q35
networks:
- name: default
pod: {}
terminationGracePeriodSeconds: 0
volumes:
- dataVolume:
name: test-dv
name: volume0
- cloudInitNoCloud:
networkData: |-
ethernets:
eth0:
addresses:
- fd10:0:2::2/120
dhcp4: true
gateway6: fd10:0:2::1
match: {}
nameservers:
addresses:
- 10.96.0.10
search:
- default.svc.cluster.local
- svc.cluster.local
- cluster.local
version: 2
name: volume1
137 changes: 86 additions & 51 deletions tests/vm_backup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,63 +200,98 @@ var _ = Describe("[smoke] VM Backup", func() {
})

Context("VM and VMI object graph backup", func() {
It("[test_id:10270]with instancetype and preference", Label("PartnerComp"), func() {
By("Create instancetype and preference")
err := f.CreateInstancetype()
Expect(err).ToNot(HaveOccurred())
err = f.CreatePreference()
Expect(err).ToNot(HaveOccurred())

By("Starting a VM")
err = f.CreateVMWithInstancetypeAndPreference()
Expect(err).ToNot(HaveOccurred())
vm, err = framework.WaitVirtualMachineRunning(f.KvClient, f.Namespace.Name, "test-vm-with-instancetype-and-preference", dvName)
Expect(err).ToNot(HaveOccurred())

By("Wait instance type controller revision to be updated on VM spec")
Eventually(func(g Gomega) {
vm, err = f.KvClient.VirtualMachine(f.Namespace.Name).Get(context.Background(), vm.Name, &metav1.GetOptions{})
g.Expect(err).ToNot(HaveOccurred())
g.Expect(vm.Spec.Instancetype.RevisionName).ToNot(BeEmpty())
g.Expect(vm.Spec.Preference.RevisionName).ToNot(BeEmpty())
_, err := f.KvClient.AppsV1().ControllerRevisions(f.Namespace.Name).Get(context.Background(), vm.Spec.Instancetype.RevisionName, metav1.GetOptions{})
g.Expect(err).ToNot(HaveOccurred())
_, err = f.KvClient.AppsV1().ControllerRevisions(f.Namespace.Name).Get(context.Background(), vm.Spec.Preference.RevisionName, metav1.GetOptions{})
g.Expect(err).ToNot(HaveOccurred())
}, 2*time.Minute, 2*time.Second).Should(Succeed())
Context("with instancetypes and preferences", func() {
nsDelFunc := func() {
err := f.KvClient.VirtualMachineInstancetype(f.Namespace.Name).
Delete(context.Background(), instancetypeName, metav1.DeleteOptions{})
Expect(err).ToNot(HaveOccurred())
err = f.KvClient.VirtualMachinePreference(f.Namespace.Name).
Delete(context.Background(), preferenceName, metav1.DeleteOptions{})
Expect(err).ToNot(HaveOccurred())
}

By("Creating backup")
err = f.RunBackupScript(timeout, backupName, "", "a.test.label=included", f.Namespace.Name, snapshotLocation, f.BackupNamespace)
Expect(err).ToNot(HaveOccurred())
clusterDelFunc := func() {
err := f.KvClient.VirtualMachineClusterInstancetype().
Delete(context.Background(), instancetypeName, metav1.DeleteOptions{})
Expect(err).ToNot(HaveOccurred())
err = f.KvClient.VirtualMachineClusterPreference().
Delete(context.Background(), preferenceName, metav1.DeleteOptions{})
Expect(err).ToNot(HaveOccurred())
}

By("Deleting VM, instancetype and preference")
err = f.KvClient.VirtualMachineInstancetype(f.Namespace.Name).
Delete(context.Background(), instancetypeName, metav1.DeleteOptions{})
Expect(err).ToNot(HaveOccurred())
err = f.KvClient.VirtualMachinePreference(f.Namespace.Name).
Delete(context.Background(), preferenceName, metav1.DeleteOptions{})
Expect(err).ToNot(HaveOccurred())
ok, err := framework.DeleteVirtualMachineAndWait(f.KvClient, f.Namespace.Name, vm.Name)
Expect(err).ToNot(HaveOccurred())
Expect(ok).To(BeTrue())
clusterCleanup := func() {
err := f.KvClient.VirtualMachineClusterInstancetype().
Delete(context.Background(), instancetypeName, metav1.DeleteOptions{})
if err != nil {
Expect(errors.IsNotFound(err)).To(BeTrue())
}
err = f.KvClient.VirtualMachineClusterPreference().
Delete(context.Background(), preferenceName, metav1.DeleteOptions{})
if err != nil {
Expect(errors.IsNotFound(err)).To(BeTrue())
}
}

// Wait until ControllerRevision is deleted
Eventually(func(g Gomega) metav1.StatusReason {
_, err := f.KvClient.AppsV1().ControllerRevisions(f.Namespace.Name).Get(context.Background(), vm.Spec.Instancetype.RevisionName, metav1.GetOptions{})
if err != nil && errors.ReasonForError(err) != metav1.StatusReasonNotFound {
return errors.ReasonForError(err)
DescribeTable("with instancetype and preference", Label("PartnerComp"), func(itFunc func() error, pFunc func() error, vmFunc func() error, delFunc func(), cleanupFunc func()) {
if cleanupFunc != nil {
defer cleanupFunc()
}
_, err = f.KvClient.AppsV1().ControllerRevisions(f.Namespace.Name).Get(context.Background(), vm.Spec.Preference.RevisionName, metav1.GetOptions{})
return errors.ReasonForError(err)
}, 2*time.Minute, 2*time.Second).Should(Equal(metav1.StatusReasonNotFound))
By("Create instancetype and preference")
err := itFunc()
Expect(err).ToNot(HaveOccurred())
err = pFunc()
Expect(err).ToNot(HaveOccurred())

By("Starting a VM")
err = vmFunc()
Expect(err).ToNot(HaveOccurred())
vm, err = framework.WaitVirtualMachineRunning(f.KvClient, f.Namespace.Name, "test-vm-with-instancetype-and-preference", dvName)
Expect(err).ToNot(HaveOccurred())

By("Wait instance type controller revision to be updated on VM spec")
Eventually(func(g Gomega) {
vm, err = f.KvClient.VirtualMachine(f.Namespace.Name).Get(context.Background(), vm.Name, &metav1.GetOptions{})
g.Expect(err).ToNot(HaveOccurred())
g.Expect(vm.Spec.Instancetype.RevisionName).ToNot(BeEmpty())
g.Expect(vm.Spec.Preference.RevisionName).ToNot(BeEmpty())
_, err := f.KvClient.AppsV1().ControllerRevisions(f.Namespace.Name).Get(context.Background(), vm.Spec.Instancetype.RevisionName, metav1.GetOptions{})
g.Expect(err).ToNot(HaveOccurred())
_, err = f.KvClient.AppsV1().ControllerRevisions(f.Namespace.Name).Get(context.Background(), vm.Spec.Preference.RevisionName, metav1.GetOptions{})
g.Expect(err).ToNot(HaveOccurred())
}, 2*time.Minute, 2*time.Second).Should(Succeed())

By("Creating backup")
err = f.RunBackupScript(timeout, backupName, "", "a.test.label=included", f.Namespace.Name, snapshotLocation, f.BackupNamespace)
Expect(err).ToNot(HaveOccurred())

By("Deleting VM, instancetype and preference")
delFunc()

ok, err := framework.DeleteVirtualMachineAndWait(f.KvClient, f.Namespace.Name, vm.Name)
Expect(err).ToNot(HaveOccurred())
Expect(ok).To(BeTrue())

// Wait until ControllerRevision is deleted
Eventually(func(g Gomega) metav1.StatusReason {
_, err := f.KvClient.AppsV1().ControllerRevisions(f.Namespace.Name).Get(context.Background(), vm.Spec.Instancetype.RevisionName, metav1.GetOptions{})
if err != nil && errors.ReasonForError(err) != metav1.StatusReasonNotFound {
return errors.ReasonForError(err)
}
_, err = f.KvClient.AppsV1().ControllerRevisions(f.Namespace.Name).Get(context.Background(), vm.Spec.Preference.RevisionName, metav1.GetOptions{})
return errors.ReasonForError(err)
}, 2*time.Minute, 2*time.Second).Should(Equal(metav1.StatusReasonNotFound))

By("Creating restore")
err = f.RunRestoreScript(timeout, backupName, restoreName, f.BackupNamespace)
Expect(err).ToNot(HaveOccurred())
By("Creating restore")
err = f.RunRestoreScript(timeout, backupName, restoreName, f.BackupNamespace)
Expect(err).ToNot(HaveOccurred())

By("Verifying VM")
err = framework.WaitForVirtualMachineStatus(f.KvClient, f.Namespace.Name, vm.Name, kvv1.VirtualMachineStatusRunning)
Expect(err).ToNot(HaveOccurred())
By("Verifying VM")
err = framework.WaitForVirtualMachineStatus(f.KvClient, f.Namespace.Name, vm.Name, kvv1.VirtualMachineStatusRunning)
Expect(err).ToNot(HaveOccurred())
},
Entry("[test_id:10270]namespace scope", f.CreateInstancetype, f.CreatePreference, f.CreateVMWithInstancetypeAndPreference, nsDelFunc, nil),
Entry("[test_id:10274]cluster scope", f.CreateClusterInstancetype, f.CreateClusterPreference, f.CreateVMWithClusterInstancetypeAndClusterPreference, clusterDelFunc, clusterCleanup),
)
})

It("[test_id:10271]with configmap, secret and serviceaccount", Label("PartnerComp"), func() {
Expand Down

0 comments on commit a8423da

Please sign in to comment.