diff --git a/virtsecrets/templates/create_secret_block.html b/virtsecrets/templates/create_secret_block.html index 9f8ed5bf..34b7003d 100644 --- a/virtsecrets/templates/create_secret_block.html +++ b/virtsecrets/templates/create_secret_block.html @@ -46,8 +46,7 @@
- +
diff --git a/virtsecrets/tests/__init__.py b/virtsecrets/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/virtsecrets/tests/test_create_secret.py b/virtsecrets/tests/test_create_secret.py new file mode 100644 index 00000000..7ed716db --- /dev/null +++ b/virtsecrets/tests/test_create_secret.py @@ -0,0 +1,28 @@ +"""A secret's usage value goes into its XML as text, whatever it holds: a +volume path, an iSCSI IQN or a Ceph name, and characters XML gives a meaning.""" + +from unittest.mock import patch + +from django.test import SimpleTestCase +from lxml import etree +from vrtManager.virtsecrets import wvmSecrets + + +class CreateSecretTests(SimpleTestCase): + def defined_xml(self, usage_type, data): + with patch("vrtManager.connection.connection_manager.get_connection") as get: + wvmSecrets("host", "login", "pw", 1).create_secret("no", "yes", usage_type, data) + return etree.fromstring(get.return_value.secretDefineXML.call_args.args[0]) + + def test_the_usage_value_is_text_of_its_element(self): + for usage_type, element, data in ( + ("volume", "volume", "/var/lib/libvirt/images/vm-1.qcow2"), + ("iscsi", "target", "iqn.2026-10.test.wvc:lun"), + ("ceph", "name", "client.libvirt secret"), + ("volume", "volume", "/a'b&d\""), + ): + with self.subTest(usage_type=usage_type, data=data): + secret = self.defined_xml(usage_type, data) + self.assertEqual((secret.get("ephemeral"), secret.get("private")), ("no", "yes")) + self.assertEqual(secret.find("usage").get("type"), usage_type) + self.assertEqual(secret.findtext(f"usage/{element}"), data) diff --git a/virtsecrets/tests/test_live_secrets.py b/virtsecrets/tests/test_live_secrets.py new file mode 100644 index 00000000..f55bc7f4 --- /dev/null +++ b/virtsecrets/tests/test_live_secrets.py @@ -0,0 +1,52 @@ +"""Live check that the secrets page creates a secret for each usage type +with a real usage value: a volume path, an iSCSI IQN and a Ceph name. Runs +only with TEST_LIBVIRT_HOST set.""" + +import os +import unittest + +import libvirt +from computes.models import Compute +from django.contrib.auth import get_user_model +from django.test import TestCase +from django.urls import reverse +from instances.tests import livetest +from vrtManager.connection import connection_manager + +P = livetest.PREFIX +USAGES = { + libvirt.VIR_SECRET_USAGE_TYPE_VOLUME: ("volume", f"/var/lib/libvirt/{P}secrets/vm-1.qcow2"), + libvirt.VIR_SECRET_USAGE_TYPE_ISCSI: ("iscsi", f"iqn.2026-10.test.wvc:{P}secret"), + libvirt.VIR_SECRET_USAGE_TYPE_CEPH: ("ceph", f"client.{P}secret secret"), +} + + +@unittest.skipUnless(livetest.enabled(), "Set TEST_LIBVIRT_HOST to run the live libvirt tests") +class LiveSecretsTestCase(TestCase): + def setUp(self): + args = dict( + hostname=os.environ["TEST_LIBVIRT_HOST"], + login=os.environ.get("TEST_LIBVIRT_LOGIN", ""), + password=os.environ.get("TEST_LIBVIRT_PASSWORD", ""), + type=int(os.environ.get("TEST_LIBVIRT_TYPE", 4)), + ) + self.conn = connection_manager.get_connection(*args.values()) + self.remove_secrets() + self.addCleanup(self.remove_secrets) + self.compute = Compute.objects.create(name="live-secrets", **args) + self.client.force_login(get_user_model().objects.create_superuser("live-secrets", "ls@example.com", "x")) + + def remove_secrets(self): + for secret in self.conn.listAllSecrets(): + if (secret.usageType(), secret.usageID()) in {(t, v) for t, (_, v) in USAGES.items()}: + secret.undefine() + + def test_each_usage_type_takes_a_real_value(self): + for usage_type, (name, value) in USAGES.items(): + with self.subTest(name): + response = self.client.post( + reverse("virtsecrets", args=[self.compute.id]), + {"create": "", "ephemeral": "no", "private": "no", "usage_type": name, "data": value}, + ) + self.assertEqual(response.status_code, 302) + self.assertEqual(self.conn.secretLookupByUsage(usage_type, value).usageID(), value) diff --git a/vrtManager/virtsecrets.py b/vrtManager/virtsecrets.py index c8772823..1117e7e5 100644 --- a/vrtManager/virtsecrets.py +++ b/vrtManager/virtsecrets.py @@ -1,21 +1,19 @@ import base64 +from lxml import etree from vrtManager.connection import wvmConnect +# the element of that holds the value, per usage type +USAGE_ELEMENTS = {"ceph": "name", "volume": "volume", "iscsi": "target"} + class wvmSecrets(wvmConnect): def create_secret(self, ephemeral, private, secret_type, data): - xml = f""" - """ - if secret_type == "ceph": - xml += f"""{data}""" - if secret_type == "volume": - xml += f"""{data}""" - if secret_type == "iscsi": - xml += f"""{data}""" - xml += """ - """ - self.wvm.secretDefineXML(xml) + # built with lxml: data is text, whatever characters it holds + secret = etree.Element("secret", ephemeral=ephemeral, private=private) + usage = etree.SubElement(secret, "usage", type=secret_type) + etree.SubElement(usage, USAGE_ELEMENTS[secret_type]).text = data + self.wvm.secretDefineXML(etree.tostring(secret, encoding="unicode")) def get_secret_value(self, uuid): secrt = self.get_secret(uuid)