Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 62 additions & 1 deletion tests/data_protection/oadp/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import pytest
from ocp_resources.datavolume import DataVolume
from ocp_resources.namespace import Namespace
from ocp_resources.virtual_machine_cluster_instancetype import VirtualMachineClusterInstancetype
from ocp_resources.virtual_machine_cluster_preference import VirtualMachineClusterPreference

from tests.data_protection.oadp.utils import (
VeleroRestore,
Expand All @@ -10,9 +12,12 @@
BACKUP_STORAGE_LOCATION,
FILE_NAME_FOR_BACKUP,
OS_FLAVOR_RHEL,
RHEL10_PREFERENCE,
SKIP_BACKUP_HOOKS_ANNOTATION,
TEXT_TO_TEST,
TIMEOUT_8MIN,
TIMEOUT_15MIN,
U1_SMALL,
Images,
)
from utilities.infra import create_ns
Expand All @@ -24,11 +29,12 @@
check_upload_virtctl_result,
create_dv,
create_vm_from_dv,
data_volume_template_with_source_ref_dict,
get_downloaded_artifact,
virtctl_upload_dv,
write_file,
)
from utilities.virt import running_vm
from utilities.virt import VirtualMachineForTests, running_vm


@pytest.fixture()
Expand Down Expand Up @@ -254,3 +260,58 @@ def velero_restore_second_namespace_with_datamover(
timeout=TIMEOUT_15MIN,
) as restore:
yield restore


@pytest.fixture()
def namespace_for_hooks_backup(admin_client, unprivileged_client):
"""Namespace for hooks opt-out tests, created with unprivileged RBAC."""
yield from create_ns(admin_client=admin_client, unprivileged_client=unprivileged_client, name="velero-hooks-ns")


@pytest.fixture()
def rhel_vm_with_hooks_opt_out(
unprivileged_client,
rhel10_data_source_scope_session,
snapshot_storage_class_name_scope_module,
namespace_for_hooks_backup,
):
"""Running RHEL VM with kubevirt.io/skip-backup-hooks annotation set to 'true'.

Creates a RHEL VM with the skip-backup-hooks annotation, waits for it to
reach a running state, and verifies the annotation is present before yielding.

Yields:
VirtualMachineForTests: Running VM with backup hooks opt-out annotation.
"""
with VirtualMachineForTests(
name="vm-hooks-opt-out",
namespace=namespace_for_hooks_backup.name,
client=unprivileged_client,
os_flavor=OS_FLAVOR_RHEL,
vm_instance_type=VirtualMachineClusterInstancetype(client=unprivileged_client, name=U1_SMALL),
vm_preference=VirtualMachineClusterPreference(client=unprivileged_client, name=RHEL10_PREFERENCE),
data_volume_template=data_volume_template_with_source_ref_dict(
data_source=rhel10_data_source_scope_session,
storage_class=snapshot_storage_class_name_scope_module,
),
annotations={SKIP_BACKUP_HOOKS_ANNOTATION: "true"},
) as vm:
running_vm(vm=vm)
assert vm.instance.metadata.annotations[SKIP_BACKUP_HOOKS_ANNOTATION] == "true", (
f"VM {vm.name} missing {SKIP_BACKUP_HOOKS_ANNOTATION} annotation"
)
yield vm


@pytest.fixture()
def paused_rhel_vm_with_hooks_opt_out(rhel_vm_with_hooks_opt_out):
"""Paused RHEL VM with kubevirt.io/skip-backup-hooks annotation set to 'true'.

Pauses the running VM from rhel_vm_with_hooks_opt_out and yields it
in the paused state.

Yields:
VirtualMachineForTests: Paused VM with backup hooks opt-out annotation.
"""
rhel_vm_with_hooks_opt_out.vmi.pause(wait=True)
yield rhel_vm_with_hooks_opt_out
91 changes: 91 additions & 0 deletions tests/data_protection/oadp/test_velero_backup_hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"""
Velero Backup Hook Opt-Out Tests

STP:
https://github.com/RedHatQE/openshift-virtualization-tests-design-docs/blob/main/stps/
sig-storage/remove-velero-hooks-stp.md
Jira: https://redhat.atlassian.net/browse/CNV-79727 # <skip-jira-utils-check>
"""

import logging

import pytest

from tests.data_protection.oadp.utils import assert_velero_backup_hooks_not_injected
from utilities.oadp import VeleroBackup

LOGGER = logging.getLogger(__name__)


class TestVeleroBackupHookOptOut:
"""
Tests for Velero backup hook opt-out.

The skip-backup-hooks annotation is intended for metadata-only backup workflows where
third-party solutions handle the actual data protection. These tests verify that
freeze/unfreeze hooks are not injected and that Velero backup completes when the
annotation is set.

Preconditions:
- Under-test VM with ``kubevirt.io/skip-backup-hooks`` set to ``"true"``
"""

@pytest.mark.polarion("CNV-16267")
def test_backup_paused_vm_hooks_disabled(
self,
admin_client,
namespace_for_hooks_backup,
paused_rhel_vm_with_hooks_opt_out,
):
"""
Test that backup of paused VM completes with hooks disabled.

Preconditions:
- Under-test VM with ``kubevirt.io/skip-backup-hooks`` set to ``"true"``, paused

Steps:
1. Run Velero backup
2. Inspect virt-launcher pod for Velero hook annotations

Expected:
- No freeze/unfreeze hooks are injected on the virt-launcher pod
- Backup completes successfully
"""
with VeleroBackup(
name="backup-paused-optout",
client=admin_client,
included_namespaces=[namespace_for_hooks_backup.name],
teardown=True,
) as backup:
assert_velero_backup_hooks_not_injected(vm=paused_rhel_vm_with_hooks_opt_out, admin_client=admin_client)
LOGGER.info(f"Backup {backup.name} completed for paused VM with opt-out annotation")

@pytest.mark.polarion("CNV-16268")
def test_backup_running_vm_hooks_disabled(
self,
admin_client,
namespace_for_hooks_backup,
rhel_vm_with_hooks_opt_out,
):
"""
Test that backup of a running VM completes with hooks disabled.

Preconditions:
- Under-test VM with ``kubevirt.io/skip-backup-hooks`` set to ``"true"``, running

Steps:
1. Run Velero backup
2. Inspect virt-launcher pod for Velero hook annotations

Expected:
- No freeze/unfreeze hooks are injected on the virt-launcher pod
- Backup completes successfully
"""
with VeleroBackup(
name="backup-hooks-opt-out",
client=admin_client,
included_namespaces=[namespace_for_hooks_backup.name],
teardown=True,
) as backup:
assert_velero_backup_hooks_not_injected(vm=rhel_vm_with_hooks_opt_out, admin_client=admin_client)
LOGGER.info(f"Backup {backup.name} completed for running VM with opt-out annotation")
36 changes: 36 additions & 0 deletions tests/data_protection/oadp/utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import logging
from typing import TYPE_CHECKING

from ocp_resources.persistent_volume_claim import PersistentVolumeClaim
from ocp_resources.restore import Restore
from ocp_resources.storage_profile import StorageProfile
from ocp_resources.virtual_machine_instance import VirtualMachineInstance

from utilities import console
from utilities.constants import (
Expand All @@ -14,12 +16,18 @@
TIMEOUT_10SEC,
TIMEOUT_15SEC,
TIMEOUT_20SEC,
VELERO_BACKUP_HOOK_ANNOTATIONS,
)
from utilities.infra import (
unique_name,
)
from utilities.oadp import delete_velero_resource

if TYPE_CHECKING:
from kubernetes.dynamic import DynamicClient

from utilities.virt import VirtualMachineForTests

LOGGER = logging.getLogger(__name__)


Expand Down Expand Up @@ -81,3 +89,31 @@ def is_storage_class_support_volume_mode(storage_class_name, requested_volume_mo
def wait_for_restored_dv(dv):
dv.pvc.wait_for_status(status=PersistentVolumeClaim.Status.BOUND, timeout=TIMEOUT_15SEC)
dv.wait_for_dv_success(timeout=TIMEOUT_10SEC)


def assert_velero_backup_hooks_not_injected(vm: VirtualMachineForTests, admin_client: DynamicClient) -> None:
"""Assert virt-launcher has no Velero freeze/unfreeze hook annotations.

Absence of these annotations means Velero will not execute filesystem
freeze/unfreeze during backup.

Args:
vm: VirtualMachine whose virt-launcher pod is checked.
admin_client: Privileged client used to access the virt-launcher pod.

Raises:
AssertionError: If any Velero hook annotations are found on the virt-launcher pod.
"""
virt_launcher_pod = VirtualMachineInstance(
client=admin_client,
name=vm.name,
namespace=vm.namespace,
).virt_launcher_pod
pod_annotations = virt_launcher_pod.instance.metadata.annotations or {}
present_hook_annotations = [
annotation_key for annotation_key in VELERO_BACKUP_HOOK_ANNOTATIONS if annotation_key in pod_annotations
]
assert not present_hook_annotations, (
f"VM {vm.name} virt-launcher pod has Velero hook annotations {present_hook_annotations} "
f"but backup hooks should be disabled"
)
11 changes: 11 additions & 0 deletions utilities/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,17 @@ class NamespacesNames:
FILE_NAME_FOR_BACKUP = "file_before_backup.txt"
TEXT_TO_TEST = "text"
BACKUP_STORAGE_LOCATION = "dpa-1"
SKIP_BACKUP_HOOKS_ANNOTATION = "kubevirt.io/skip-backup-hooks"

# Velero hook annotations injected on virt-launcher when backup hooks are enabled.
# See kubevirt pkg/storage/velero and pkg/storage/pod/annotations/generator.go
VELERO_BACKUP_HOOK_ANNOTATIONS = (
"pre.hook.backup.velero.io/container",
"pre.hook.backup.velero.io/command",
"pre.hook.backup.velero.io/timeout",
"post.hook.backup.velero.io/container",
"post.hook.backup.velero.io/command",
)

# AAQ
AAQ_NAMESPACE_LABEL = {"application-aware-quota/enable-gating": ""}
Expand Down