Skip to content

Commit

Permalink
test: update tests to allow ssh keys
Browse files Browse the repository at this point in the history
With ssh a root login is only possible via a sshkey. So let's
support this so that we can run `bootc status` which requires
root privs.
With the switch to bootc we need to adjust the testing. We inject
a root ssh key now and just use that for login.
  • Loading branch information
mvo5 committed May 7, 2024
1 parent 8ea549f commit e332166
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 12 deletions.
41 changes: 31 additions & 10 deletions test/test_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class ImageBuildResult(NamedTuple):
container_ref: str
username: str
password: str
ssh_keyfile_private_path: str
kargs: str
bib_output: str
journal_output: str
Expand Down Expand Up @@ -139,6 +140,10 @@ def build_images(shared_tmpdir, build_container, request, force_aws_upload):

journal_log_path = output_path / "journal.log"
bib_output_path = output_path / "bib-output.log"

ssh_keyfile_private_path = output_path / "ssh-keyfile"
ssh_keyfile_public_path = ssh_keyfile_private_path.with_suffix(".pub")

artifact = {
"qcow2": pathlib.Path(output_path) / "qcow2/disk.qcow2",
"ami": pathlib.Path(output_path) / "image/disk.raw",
Expand Down Expand Up @@ -171,9 +176,21 @@ def build_images(shared_tmpdir, build_container, request, force_aws_upload):
bib_output = bib_output_path.read_text(encoding="utf8")
results.append(ImageBuildResult(
image_type, generated_img, target_arch, container_ref,
username, password,
username, password, ssh_keyfile_private_path,
kargs, bib_output, journal_output))

# generate new keyfile
if not ssh_keyfile_private_path.exists():
subprocess.run([
"ssh-keygen",
"-N", "",
# be very conservative with keys for paramiko
"-b", "2048",
"-t", "rsa",
"-f", os.fspath(ssh_keyfile_private_path),
], check=True)
ssh_pubkey = ssh_keyfile_public_path.read_text(encoding="utf8").strip()

# Because we always build all image types, regardless of what was requested, we should either have 0 results or all
# should be available, so if we found at least one result but not all of them, this is a problem with our setup
assert not results or len(results) == len(image_types), \
Expand All @@ -190,14 +207,19 @@ def build_images(shared_tmpdir, build_container, request, force_aws_upload):
"customizations": {
"user": [
{
"name": "root",
"key": ssh_pubkey,
# cannot use default /root as is on a read-only place
"home": "/var/roothome",
}, {
"name": username,
"password": password,
"groups": ["wheel"],
},
],
"kernel": {
"append": kargs,
}
},
},
}

Expand Down Expand Up @@ -290,7 +312,7 @@ def del_ami():
for image_type in image_types:
results.append(ImageBuildResult(
image_type, artifact[image_type], target_arch, container_ref,
username, password,
username, password, ssh_keyfile_private_path,
kargs, bib_output, journal_output, metadata))
yield results

Expand Down Expand Up @@ -335,18 +357,17 @@ def assert_kernel_args(test_vm, image_type):
@pytest.mark.parametrize("image_type", gen_testcases("qemu-boot"), indirect=["image_type"])
def test_image_boots(image_type):
with QEMU(image_type.img_path, arch=image_type.img_arch) as test_vm:
# user/password login works
exit_status, _ = test_vm.run("true", user=image_type.username, password=image_type.password)
assert exit_status == 0
exit_status, output = test_vm.run("echo hello", user=image_type.username, password=image_type.password)
# root/ssh login also works
exit_status, output = test_vm.run("id", user="root", keyfile=image_type.ssh_keyfile_private_path)
assert exit_status == 0
assert "hello" in output
assert "uid=0" in output
# check generic image options
assert_kernel_args(test_vm, image_type)
# ensure bootc points to the right image
# TODO: replace this ssh root instead login, see PR#357
_, output = test_vm.run(
f"echo {image_type.password} | sudo -S bootc status",
user=image_type.username, password=image_type.password,
)
_, output = test_vm.run("bootc status", user="root", keyfile=image_type.ssh_keyfile_private_path)
# XXX: read the fully yaml instead?
assert f"image: {image_type.container_ref}" in output

Expand Down
10 changes: 8 additions & 2 deletions test/vm.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import abc
import os
import paramiko
import pathlib
import platform
import subprocess
Expand Down Expand Up @@ -43,16 +44,21 @@ def force_stop(self):
Stop the VM and clean up any resources that were created when setting up and starting the machine.
"""

def run(self, cmd, user, password):
def run(self, cmd, user, password="", keyfile=None):
"""
Run a command on the VM via SSH using the provided credentials.
"""
if not self.running():
self.start()
client = SSHClient()
client.set_missing_host_key_policy(AutoAddPolicy)
# workaround, see https://github.com/paramiko/paramiko/issues/2048
pkey = None
if keyfile:
pkey = paramiko.RSAKey.from_private_key_file(keyfile)
client.connect(
self._address, self._ssh_port, user, password,
self._address, self._ssh_port,
user, password, pkey=pkey,
allow_agent=False, look_for_keys=False)
chan = client.get_transport().open_session()
chan.get_pty()
Expand Down

0 comments on commit e332166

Please sign in to comment.