diff --git a/kernel_install_dep.sh b/kernel_install_dep.sh index cf7122a..e65764c 100755 --- a/kernel_install_dep.sh +++ b/kernel_install_dep.sh @@ -10,6 +10,9 @@ install_kselftest_deps_8() { echo sudo dnf -y groupinstall 'Development Tools' sudo dnf -y install epel-release + # EPEL repos use $releasever which breaks when vault-pinned to a minor version (e.g. 8.6) + sudo sed -i 's/\$releasever/8/g' /etc/yum.repos.d/epel*.repo + sudo dnf config-manager --set-enabled powertools sudo dnf -y install --enablerepo=devel \ VirtualGL \ alsa-lib-devel \ @@ -43,9 +46,11 @@ install_kselftest_deps_8() { nftables \ nmap-ncat \ numactl-devel \ + openssl \ openssl-devel \ perf \ popt-devel \ + python3 \ python3-pip \ rsync \ socat \ @@ -71,6 +76,9 @@ install_kselftest_deps_8() { pyyaml \ scapy \ tftpy + + sudo dnf -y update + sudo systemctl restart sshd } install_kselftest_deps_9() { @@ -79,8 +87,9 @@ install_kselftest_deps_9() { echo sudo dnf -y groupinstall 'Development Tools' sudo dnf -y install epel-release + # EPEL repos use $releasever which breaks when vault-pinned to a minor version (e.g. 9.2) + sudo sed -i 's/\$releasever/9/g' /etc/yum.repos.d/epel*.repo sudo dnf -y install --enablerepo=crb,devel \ - VirtualGL \ alsa-lib-devel \ bc \ clang \ @@ -92,7 +101,6 @@ install_kselftest_deps_9() { ethtool \ fuse \ fuse-devel \ - gcc-toolset-13-libasan-devel \ glibc \ glibc-static \ iperf3 \ @@ -126,9 +134,15 @@ install_kselftest_deps_9() { tcpdump \ teamd \ traceroute \ - virtme-ng \ wget + # Not available on all 9.x minor releases + sudo dnf -y install --enablerepo=crb,devel \ + VirtualGL \ + gcc-toolset-13-libasan-devel \ + virtme-ng \ + || echo "Optional packages not available or install failed; continuing." + pip3 install --user \ jsonschema \ netaddr \ @@ -140,6 +154,9 @@ install_kselftest_deps_9() { scapy \ tftpy \ wheel + + sudo dnf -y update + sudo systemctl restart sshd } install_kselftest_deps_10() { @@ -208,6 +225,9 @@ install_kselftest_deps_10() { scapy \ tftpy \ wheel + + sudo dnf -y update + sudo systemctl restart sshd } case "${VERSION_ID%%.*}" in diff --git a/kt/commands/vm/command.py b/kt/commands/vm/command.py index 0715b59..2b76d5a 100644 --- a/kt/commands/vm/command.py +++ b/kt/commands/vm/command.py @@ -71,8 +71,9 @@ @click.option("--test", is_flag=True, help="Build the kernel and run kselftests") @click.option("--vcpus", type=int, default=12, help="Number of virtual CPUs (default: 12)") @click.option("--memory", type=int, default=32768, help="Memory in MiB (default: 32768)") +@click.option("--no-depot", is_flag=True, help="Skip depot client installation and channel setup") @click.argument("kernel_workspace", required=False, shell_complete=ShellCompletion.show_kernel_workspaces) -def vm(kernel_workspace, console, destroy, override, override_base, list_all, test, vcpus, memory): +def vm(kernel_workspace, console, destroy, override, override_base, list_all, test, vcpus, memory, no_depot): if not list_all and not kernel_workspace: raise click.UsageError("kernel_workspace is required unless --list-all is specified") @@ -86,4 +87,5 @@ def vm(kernel_workspace, console, destroy, override, override_base, list_all, te test=test, vcpus=vcpus, memory=memory, + no_depot=no_depot, ) diff --git a/kt/commands/vm/impl.py b/kt/commands/vm/impl.py index 2c125fc..cd92475 100644 --- a/kt/commands/vm/impl.py +++ b/kt/commands/vm/impl.py @@ -16,6 +16,7 @@ def main( test: bool = False, vcpus: int = 12, memory: int = 32768, + no_depot: bool = False, ): if list_all: VmCommand.list_all() @@ -31,7 +32,12 @@ def main( return vm_instance = Vm.setup_and_spinup( - kernel_workspace_name=name, override=override, override_base=override_base, vcpus=vcpus, memory=memory + kernel_workspace_name=name, + override=override, + override_base=override_base, + vcpus=vcpus, + memory=memory, + no_depot=no_depot, ) config = Config.load() diff --git a/kt/data/cloud_init.yaml b/kt/data/cloud_init.yaml index 77101c4..2f983ab 100644 --- a/kt/data/cloud_init.yaml +++ b/kt/data/cloud_init.yaml @@ -15,8 +15,14 @@ ssh_pwauth: true package_upgrade: False mounts: - - ["mount_tag_mock_scratch", "SHARED_DIR_PLACEHOLDER", "virtiofs", "rw,relatime,context=unconfined_u:object_r:mock_var_lib_t:s0"] - - ["SHARED_DIR_PLACEHOLDER", "/var/lib/mock", "bind", "defaults,bind"] + - - mount_tag_mock_scratch + - SHARED_DIR_PLACEHOLDER + - virtiofs + - rw,relatime,context=unconfined_u:object_r:mock_var_lib_t:s0 + - - SHARED_DIR_PLACEHOLDER + - /var/lib/mock + - bind + - defaults,bind # Change working directory after boot # Setting up homedir does not work without tricks, because @@ -32,4 +38,12 @@ write_files: # and then root is the owner # workaround to change the owner to user runcmd: - - [chown, USER_PLACEHOLDER:USER_PLACEHOLDER, HOMEDIR_PLACEHOLDER] + - - chown + - USER_PLACEHOLDER:USER_PLACEHOLDER + - HOMEDIR_PLACEHOLDER + +power_state: + mode: reboot + message: "Rebooting after install" + timeout: 30 + condition: true diff --git a/kt/data/kernels.yaml b/kt/data/kernels.yaml index c13c057..2095ee6 100644 --- a/kt/data/kernels.yaml +++ b/kt/data/kernels.yaml @@ -19,6 +19,9 @@ kernels: dist_git_branch: lts86-8 mock_config: rocky-lts86 automated: true + vm_image_url: https://dl.rockylinux.org/vault/rocky/8.6/images/Rocky-8-GenericCloud-8.6.20220702.0.x86_64.qcow2 + depot_channels: + - lts-8.6 lts-9.2: src_tree_root: kernel-src-tree @@ -27,6 +30,9 @@ kernels: dist_git_branch: lts92-9 mock_config: rocky-lts92 automated: true + vm_image_url: https://dl.rockylinux.org/vault/rocky/9.2/images/x86_64/Rocky-9-GenericCloud-Base-9.2-20230513.0.x86_64.qcow2 + depot_channels: + - lts-9.2 lts-9.6: src_tree_root: kernel-src-tree @@ -35,6 +41,9 @@ kernels: dist_git_branch: lts96-9 mock_config: rocky-lts96 automated: true + vm_image_url: https://dl.rockylinux.org/vault/rocky/9.6/images/x86_64/Rocky-9-GenericCloud-Base-9.6-20250531.0.x86_64.qcow2 + depot_channels: + - lts-9.6 fipslegacy-8.6: src_tree_root: kernel-src-tree diff --git a/kt/ktlib/kernels.py b/kt/ktlib/kernels.py index 08dab17..e64f192 100644 --- a/kt/ktlib/kernels.py +++ b/kt/ktlib/kernels.py @@ -39,6 +39,9 @@ class KernelInfo: automated: bool + vm_image_url: str | None = None + depot_channels: list[str] | None = None + @dataclass class KernelsInfo: diff --git a/kt/ktlib/vm.py b/kt/ktlib/vm.py index bf54fc5..d9125d8 100644 --- a/kt/ktlib/vm.py +++ b/kt/ktlib/vm.py @@ -5,13 +5,16 @@ import time from dataclasses import dataclass -import oyaml as yaml import wget from git import Repo from pathlib3x import Path +from ruamel.yaml import YAML + +from urllib.parse import urlparse from kt.ktlib.config import Config from kt.ktlib.kernel_workspace import KernelWorkspace +from kt.ktlib.kernels import KernelsInfo from kt.ktlib.local import LocalCommand from kt.ktlib.ssh import SshCommand from kt.ktlib.util import Constants @@ -43,18 +46,29 @@ class Vm: cloud_init_path: Path name: str kernel_workspace: KernelWorkspace + vm_image_url: str | None = None + depot_channels: list[str] | None = None @classmethod - def load(cls, config: Config, kernel_workspace: KernelWorkspace): + def load( + cls, + config: Config, + kernel_workspace: KernelWorkspace, + vm_image_url: str | None = None, + depot_channels: list[str] | None = None, + ): kernel_workspace_str = kernel_workspace.folder.name kernel_name = cls._extract_kernel_name(kernel_workspace_str) vm_major_version = cls._extract_major(kernel_name) vm_major_minor_version = cls._extract_major_minor(kernel_name) - # Image source paths construction - qcow2_source_path = config.images_source_dir / Path( - cls._qcow2_name(vm_major_minor_version=vm_major_minor_version) - ) + # Image source paths construction — use pinned URL basename if set, else default + if vm_image_url: + source_image_name = Path(urlparse(vm_image_url).path).name + else: + source_image_name = cls._qcow2_name(vm_major_minor_version=vm_major_minor_version) + + qcow2_source_path = config.images_source_dir / Path(source_image_name) # Actual current image paths construction work_dir = config.images_dir / Path(kernel_workspace_str) @@ -69,6 +83,8 @@ def load(cls, config: Config, kernel_workspace: KernelWorkspace): cloud_init_path=cloud_init_path, name=kernel_workspace_str, kernel_workspace=kernel_workspace, + vm_image_url=vm_image_url, + depot_channels=depot_channels, ) @classmethod @@ -104,7 +120,19 @@ def load_from_workspace(cls, kernel_workspace_name: str): config = Config.load() kernel_workpath = config.kernels_dir / kernel_workspace_name kernel_workspace = KernelWorkspace.load_from_filepath(folder=kernel_workpath) - return cls.load(config=config, kernel_workspace=kernel_workspace) + + vm_image_url = None + depot_channels = None + kernel_name = cls._extract_kernel_name(kernel_workspace_name) + kernels_info = KernelsInfo.from_yaml(config=config) + kernel_info = kernels_info.kernels.get(kernel_name) + if kernel_info: + vm_image_url = kernel_info.vm_image_url + depot_channels = kernel_info.depot_channels + + return cls.load( + config=config, kernel_workspace=kernel_workspace, vm_image_url=vm_image_url, depot_channels=depot_channels + ) @classmethod def setup_and_spinup( @@ -114,6 +142,7 @@ def setup_and_spinup( override_base: bool = False, vcpus: int = 12, memory: int = 32768, + no_depot: bool = False, ): """ Setup and spin up a VM from a kernel workspace name. @@ -124,6 +153,7 @@ def setup_and_spinup( override_base: If True, destroy and recreate the VM but override the base image as well vcpus: Number of virtual CPUs memory: Memory in MiB + no_depot: If True, skip depot client installation and channel setup Returns: VmInstance: The running VM instance @@ -135,11 +165,13 @@ def setup_and_spinup( vm.destroy() vm.setup(override_base=override_base) - vm_instance = vm.spin_up(config=config, vcpus=vcpus, memory=memory) + vm_instance = vm.spin_up(config=config, vcpus=vcpus, memory=memory, no_depot=no_depot) return vm_instance def _get_vm_url(self): + if self.vm_image_url: + return self.vm_image_url return f"{Constants.BASE_URL}/{self.vm_major_minor_version}/images/x86_64/{Constants.DEFAULT_VM_BASE}-{self.vm_major_version}-{Constants.QCOW2_TRAIL}" def _download_source_image(self, override_base: bool = False): @@ -156,10 +188,16 @@ def _download_source_image(self, override_base: bool = False): logging.info(f"Downloading image from {self._get_vm_url()}") wget.download(self._get_vm_url(), out=str(self.qcow2_source_path)) - def _setup_cloud_init(self, config: Config): + def _setup_cloud_init(self, config: Config, no_depot: bool = False): + yaml = YAML() + yaml.preserve_quotes = True + yaml.width = 4096 + yaml.default_flow_style = False + yaml.best_sequence_indent = 2 + data = None with open(CLOUD_INIT_BASE_PATH) as f: - data = yaml.safe_load(f) + data = yaml.load(f) # replace placeholders with user data data["users"][0]["name"] = config.user @@ -185,18 +223,39 @@ def _setup_cloud_init(self, config: Config): data["runcmd"][0][1] = f"{config.user}:{config.user}" data["runcmd"][0][2] = os.environ["HOME"] + # Pin dnf to vault for kernels with a pinned VM image (Rocky only, not CentOS/cbr) + if self.vm_image_url: + data["runcmd"].append(f'echo "{self.vm_major_minor_version}" > /etc/dnf/vars/releasever') + data["runcmd"].append('echo "vault/rocky" > /etc/dnf/vars/contentdir') + data["runcmd"].append( + "cd /etc/yum.repos.d/ && for f in *.repo; do " + 'sed -i -e "s/^mirrorlist=/#mirrorlist=/" -e "s/#baseurl=/baseurl=/" "$f"; done' + ) + data["runcmd"].append("dnf clean all") + + # Depot: install the client and login+enable if credentials are present + if not no_depot: + data["runcmd"].append( + "dnf install -y https://depot.ciq.com/public/files/depot-client/depot/depot.x86_64.rpm" + ) + depot_user = os.environ.get("DEPOT_USER") + depot_token = os.environ.get("DEPOT_TOKEN") + if depot_user and depot_token and self.depot_channels: + data["runcmd"].append(f"depot login -u {depot_user} -t {depot_token}") + for channel in self.depot_channels: + data["runcmd"].append(f"depot enable {channel} -y") + # Install packages needed later data["runcmd"].append([str(config.base_path / Path("kernel-src-tree-tools") / Path("kernel_install_dep.sh"))]) # Write this to image cloud_init with open(self.cloud_init_path, "w") as f: - f.write("#cloud-config\n") yaml.dump(data, f) - def _create_image(self, config: Config, vcpus: int = 12, memory: int = 32768): + def _create_image(self, config: Config, vcpus: int = 12, memory: int = 32768, no_depot: bool = False): # Make sure the dir exists self.qcow2_path.parent.mkdir(parents=True, exist_ok=True) - self._setup_cloud_init(config=config) + self._setup_cloud_init(config=config, no_depot=no_depot) # Copy qcow2 image to work dir self.qcow2_source_path.copy(self.qcow2_path) @@ -228,11 +287,11 @@ def _resize_disk(self): def setup(self, override_base: bool = False): self._download_source_image(override_base=override_base) - def spin_up(self, config: Config, vcpus: int = 12, memory: int = 32768) -> VmInstance: + def spin_up(self, config: Config, vcpus: int = 12, memory: int = 32768, no_depot: bool = False) -> VmInstance: if not VirtHelper.exists(vm_name=self.name): logging.info(f"VM {self.name} does not exist, creating from scratch...") - self._create_image(config=config, vcpus=vcpus, memory=memory) + self._create_image(config=config, vcpus=vcpus, memory=memory, no_depot=no_depot) return VmInstance(name=self.name, kernel_workspace=self.kernel_workspace) logging.info(f"Vm {self.name} already exists") diff --git a/pyproject.toml b/pyproject.toml index 5d73931..ac138d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,8 @@ dependencies = [ "click", "pathlib3x", "python3-wget", - "oyaml", + "ruamel.yaml", + "pyyaml", "pexpect", "jira", "requests", diff --git a/tests/kt/ktlib/test_kernels.py b/tests/kt/ktlib/test_kernels.py index 1b9c2b3..4b6b4b0 100644 --- a/tests/kt/ktlib/test_kernels.py +++ b/tests/kt/ktlib/test_kernels.py @@ -100,3 +100,29 @@ def test_kernels_from_dict_check_src_root(): kernels_info = KernelsInfo.from_dict(data=data, private_data={}, config=config) kernel_info = list(kernels_info.kernels.values())[0] assert kernel_info.src_tree_root.folder == config.base_path / Path("kernel-src-tree") + + +def test_kernels_vm_image_url_default_none(): + config = Config.from_str_dict(Config.DEFAULT) + + kernels_info = KernelsInfo.from_dict(data=data, private_data={}, config=config) + kernel_info = list(kernels_info.kernels.values())[0] + assert kernel_info.vm_image_url is None + + +def test_kernels_vm_image_url_present(): + pinned_url = ( + "https://dl.rockylinux.org/vault/rocky/9.2/images/x86_64/Rocky-9-GenericCloud-Base-9.2-20230513.0.x86_64.qcow2" + ) + kernels_with_pin = { + "kernel1": { + **kernels["kernel1"], + "vm_image_url": pinned_url, + } + } + data_with_pin = {"common_repos": common_repos, "kernels": kernels_with_pin} + config = Config.from_str_dict(Config.DEFAULT) + + kernels_info = KernelsInfo.from_dict(data=data_with_pin, private_data={}, config=config) + kernel_info = list(kernels_info.kernels.values())[0] + assert kernel_info.vm_image_url == pinned_url diff --git a/tests/kt/ktlib/test_vm.py b/tests/kt/ktlib/test_vm.py new file mode 100644 index 0000000..990bb0e --- /dev/null +++ b/tests/kt/ktlib/test_vm.py @@ -0,0 +1,227 @@ +import os +from io import StringIO +from unittest.mock import MagicMock, patch + +from ruamel.yaml import YAML +from pathlib3x import Path + +from kt.ktlib.util import Constants +from kt.ktlib.vm import Vm + +_real_open = open + + +def _make_vm(vm_image_url=None, major="9", major_minor="9.2"): + vm = Vm.__new__(Vm) + vm.vm_major_version = major + vm.vm_major_minor_version = major_minor + vm.vm_image_url = vm_image_url + return vm + + +def test_get_vm_url_default(): + vm = _make_vm() + url = vm._get_vm_url() + expected = f"{Constants.BASE_URL}/9.2/images/x86_64/{Constants.DEFAULT_VM_BASE}-9-{Constants.QCOW2_TRAIL}" + assert url == expected + + +def test_get_vm_url_pinned(): + pin = ( + "https://dl.rockylinux.org/vault/rocky/9.2/images/x86_64/Rocky-9-GenericCloud-Base-9.2-20230513.0.x86_64.qcow2" + ) + vm = _make_vm(vm_image_url=pin) + assert vm._get_vm_url() == pin + + +def test_load_cache_filename_default(): + config = MagicMock() + config.images_source_dir = Path("/tmp/images") + config.images_dir = Path("/tmp/virt-images") + + workspace = MagicMock() + workspace.folder.name = "lts-9.2" + + vm = Vm.load(config=config, kernel_workspace=workspace) + assert vm.qcow2_source_path.name == "Rocky-9.2-GenericCloud.latest.x86_64.qcow2" + + +def test_load_cache_filename_pinned(): + config = MagicMock() + config.images_source_dir = Path("/tmp/images") + config.images_dir = Path("/tmp/virt-images") + + workspace = MagicMock() + workspace.folder.name = "lts-9.2" + + pin = ( + "https://dl.rockylinux.org/vault/rocky/9.2/images/x86_64/Rocky-9-GenericCloud-Base-9.2-20230513.0.x86_64.qcow2" + ) + vm = Vm.load(config=config, kernel_workspace=workspace, vm_image_url=pin) + assert vm.qcow2_source_path.name == "Rocky-9-GenericCloud-Base-9.2-20230513.0.x86_64.qcow2" + + +def test_load_pinned_url_stored(): + config = MagicMock() + config.images_source_dir = Path("/tmp/images") + config.images_dir = Path("/tmp/virt-images") + + workspace = MagicMock() + workspace.folder.name = "lts-9.6" + + pin = ( + "https://dl.rockylinux.org/vault/rocky/9.6/images/x86_64/Rocky-9-GenericCloud-Base-9.6-20250531.0.x86_64.qcow2" + ) + vm = Vm.load(config=config, kernel_workspace=workspace, vm_image_url=pin) + assert vm.vm_image_url == pin + assert vm._get_vm_url() == pin + + +def _make_config(): + config = MagicMock() + config.user = "testuser" + config.ssh_key = "/tmp/fake_key.pub" + base = MagicMock() + base.__str__ = lambda self: "/home/testuser/ciq" + base.__truediv__ = lambda self, other: Path("/home/testuser/ciq") / other + base.absolute.return_value = Path("/home/testuser/ciq") + config.base_path = base + config.kernels_dir = Path("/home/testuser/ciq/kernels") + return config + + +def _build_vm_and_get_runcmd(vm_image_url=None, depot_channels=None, depot_env=None, no_depot=False): + """Build a Vm, call _setup_cloud_init, and capture the generated runcmd.""" + config = _make_config() + + vm = Vm.__new__(Vm) + vm.vm_major_version = "8" + vm.vm_major_minor_version = "8.6" + vm.vm_image_url = vm_image_url + vm.depot_channels = depot_channels + vm.name = "lts-8.6" + vm.cloud_init_path = Path("/tmp/test_cloud_init.yaml") + + env = {"HOME": "/home/testuser"} + if depot_env: + env["DEPOT_USER"] = depot_env[0] + env["DEPOT_TOKEN"] = depot_env[1] + else: + env["DEPOT_USER"] = "" + env["DEPOT_TOKEN"] = "" + + captured_yaml = {} + + def fake_open(path, mode="r"): + path_str = str(path) + if mode == "w": + buf = StringIO() + + class WriteCapture: + def __enter__(self_inner): + return buf + + def __exit__(self_inner, *args): + captured_yaml["content"] = buf.getvalue() + + return WriteCapture() + if path_str == str(config.ssh_key): + buf = StringIO("ssh-ed25519 TESTKEY") + + class ReadCtx: + def __enter__(self_inner): + return buf + + def __exit__(self_inner, *args): + pass + + return ReadCtx() + return _real_open(path, mode) + + with patch("builtins.open", side_effect=fake_open), patch.dict(os.environ, env): + vm._setup_cloud_init(config=config, no_depot=no_depot) + + content = captured_yaml.get("content", "") + if content.startswith("#cloud-config\n"): + content = content[len("#cloud-config\n") :] + data = YAML(typ="safe").load(content) + return data["runcmd"] + + +def test_cloud_init_vault_pin_present_when_image_pinned(): + pin = "https://dl.rockylinux.org/vault/rocky/8.6/images/Rocky-8-GenericCloud-8.6.20220702.0.x86_64.qcow2" + runcmd = _build_vm_and_get_runcmd(vm_image_url=pin) + + runcmd_strs = [str(c) for c in runcmd] + assert any("releasever" in s and "8.6" in s for s in runcmd_strs) + assert any("vault/rocky" in s for s in runcmd_strs) + assert any("mirrorlist" in s for s in runcmd_strs) + + +def test_cloud_init_no_vault_pin_when_no_image_url(): + runcmd = _build_vm_and_get_runcmd(vm_image_url=None) + + runcmd_strs = [str(c) for c in runcmd] + assert not any("releasever" in s for s in runcmd_strs) + assert not any("vault/rocky" in s for s in runcmd_strs) + + +def test_cloud_init_depot_install_present_by_default(): + runcmd = _build_vm_and_get_runcmd(vm_image_url=None) + runcmd_strs = [str(c) for c in runcmd] + assert any("depot.x86_64.rpm" in s for s in runcmd_strs) + + +def test_cloud_init_depot_login_when_creds_present(): + runcmd = _build_vm_and_get_runcmd( + depot_channels=["lts-8.6"], + depot_env=("myuser", "mytoken"), + ) + runcmd_strs = [str(c) for c in runcmd] + assert any("depot login -u myuser -t mytoken" in s for s in runcmd_strs) + assert any("depot enable lts-8.6 -y" in s for s in runcmd_strs) + + +def test_cloud_init_no_depot_login_without_creds(): + runcmd = _build_vm_and_get_runcmd(depot_channels=["lts-8.6"]) + runcmd_strs = [str(c) for c in runcmd] + assert not any("depot login" in s for s in runcmd_strs) + assert not any("depot enable" in s for s in runcmd_strs) + + +def test_cloud_init_ordering(): + """Vault pin, then depot, then kernel_install_dep.sh — in that order.""" + pin = "https://dl.rockylinux.org/vault/rocky/8.6/images/Rocky-8-GenericCloud-8.6.20220702.0.x86_64.qcow2" + runcmd = _build_vm_and_get_runcmd( + vm_image_url=pin, + depot_channels=["lts-8.6"], + depot_env=("myuser", "mytoken"), + ) + runcmd_strs = [str(c) for c in runcmd] + + vault_idx = next(i for i, s in enumerate(runcmd_strs) if "releasever" in s) + depot_install_idx = next(i for i, s in enumerate(runcmd_strs) if "depot.x86_64.rpm" in s) + depot_login_idx = next(i for i, s in enumerate(runcmd_strs) if "depot login" in s) + dep_script_idx = next(i for i, s in enumerate(runcmd_strs) if "kernel_install_dep.sh" in s) + + assert vault_idx < depot_install_idx < depot_login_idx < dep_script_idx + + +def test_cloud_init_no_depot_skips_all_depot_commands(): + """no_depot=True skips depot even when creds and channels are present.""" + runcmd = _build_vm_and_get_runcmd( + depot_channels=["lts-8.6"], + depot_env=("myuser", "mytoken"), + no_depot=True, + ) + runcmd_strs = [str(c) for c in runcmd] + assert not any("depot" in s for s in runcmd_strs) + assert any("kernel_install_dep.sh" in s for s in runcmd_strs) + + +def test_cloud_init_no_depot_skips_depot_install(): + """no_depot=True omits the depot RPM install (no creds variant).""" + runcmd = _build_vm_and_get_runcmd(no_depot=True) + runcmd_strs = [str(c) for c in runcmd] + assert not any("depot.x86_64.rpm" in s for s in runcmd_strs) + assert any("kernel_install_dep.sh" in s for s in runcmd_strs)