!208 [sync] PR-194: fix test failed

From: @openeuler-sync-bot 
Reviewed-by: @gaoruoshu 
Signed-off-by: @gaoruoshu
This commit is contained in:
openeuler-ci-bot 2024-04-08 02:54:13 +00:00 committed by Gitee
commit c95206a041
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
6 changed files with 1356 additions and 3 deletions

View File

@ -0,0 +1,73 @@
From 463d36cc237435084e5ddb4d7468f5546e7ece28 Mon Sep 17 00:00:00 2001
From: Chad Smith <chad.smith@canonical.com>
Date: Wed, 6 Mar 2024 07:51:34 -0700
Subject: [PATCH] bug(tests): mock reads of host's /sys/class/net via
get_sys_class_path
Avoid leaking reads to the underlying host's /sys/class/net files.
Some test environments contain virtual network hardware and
configuration such as Calico network devices which provide
duplicated MAC addresses for each device in /sys/class/net. This
results in errors from unittests calling cloudinit.net.get_interfaces.
Provide a disable_sysfs_net` fixture and use it in net-related
modules or functions to avoid unrelated test failures due to
environmental differences.
Provide the ability to turn off this fixture for tests which
need to write to the mocked sysfs tmp directory so test artifacts
do not pollute other tests.
This fixture can be disabled by passing False to the disable_sysfs_net
via request.param. We want to avoid polluting tox.ini with pytest.marks
for infrequently used cases like this.
Also fix whitespace in tox.ini
---
tests/unittests/conftest.py | 20 ++++++++++++++++++++
tox.ini | 2 +-
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/tests/unittests/conftest.py b/tests/unittests/conftest.py
index 91dbd06..22bc189 100644
--- a/tests/unittests/conftest.py
+++ b/tests/unittests/conftest.py
@@ -61,6 +61,26 @@ def fake_filesystem(mocker, tmpdir):
mocker.patch.object(mod, f, trap_func)
+@pytest.fixture(scope="session", autouse=True)
+def disable_sysfs_net(request, tmpdir_factory):
+ """Avoid tests which read the undertying host's /syc/class/net.
+
+ To allow unobscured reads of /sys/class/net on the host we can
+ parametrize the fixture with:
+
+ @pytest.mark.parametrize("disable_sysfs_net", [False], indirect=True)
+ """
+ if hasattr(request, "param") and getattr(request, "param") is False:
+ # Test disabled this fixture, perform no mocks.
+ yield
+ return
+ mock_sysfs = f"{tmpdir_factory.mktemp('sysfs')}/"
+ with mock.patch(
+ "cloudinit.net.get_sys_class_path", return_value=mock_sysfs
+ ):
+ yield mock_sysfs
+
+
@pytest.fixture(autouse=True)
def disable_dns_lookup(request):
if "allow_dns_lookup" in request.keywords:
diff --git a/tox.ini b/tox.ini
index 5f01a9a..cd261f3 100644
--- a/tox.ini
+++ b/tox.ini
@@ -323,4 +323,4 @@ markers =
serial: tests that do not work in parallel, skipped with py3-fast
unstable: skip this test because it is flakey
user_data: the user data to be passed to the test instance
- allow_dns_lookup: disable autochecking for host network configuration
+ allow_dns_lookup: disable autochecking for host network configuration
--
2.33.0

View File

@ -0,0 +1,457 @@
From c948e4182b9557f2befeb6210a055d0aa1c2df21 Mon Sep 17 00:00:00 2001
From: James Falcon <james.falcon@canonical.com>
Date: Wed, 14 Feb 2024 16:25:11 -0600
Subject: [PATCH] fix: unpin jsonschema and update tests (#4882)
In 034a5cd , we pinned jsonschema version due to failing tests. Test
failures were due to jsonschema library changing error messages.
This commit unpins the version and updates tests accordingly.
Fixes GH-4783
Co-authored-by: dermotbradley <dermot_bradley@yahoo.com>
---
.../unittests/config/test_cc_apk_configure.py | 5 +++--
.../unittests/config/test_cc_apt_configure.py | 14 ++++++-------
tests/unittests/config/test_cc_bootcmd.py | 17 +++++++++++-----
tests/unittests/config/test_cc_ca_certs.py | 10 +++++++---
tests/unittests/config/test_cc_chef.py | 5 +++--
tests/unittests/config/test_cc_lxd.py | 2 +-
tests/unittests/config/test_cc_mounts.py | 10 ++++++++--
.../test_cc_package_update_upgrade_install.py | 3 ++-
tests/unittests/config/test_cc_runcmd.py | 3 ++-
.../unittests/config/test_cc_set_passwords.py | 9 +++++++--
tests/unittests/config/test_cc_snap.py | 20 +++++++++++++------
tests/unittests/config/test_cc_write_files.py | 6 +++++-
.../unittests/config/test_cc_yum_add_repo.py | 2 +-
tests/unittests/helpers.py | 8 ++++++++
tests/unittests/test_merging.py | 2 +-
15 files changed, 81 insertions(+), 35 deletions(-)
diff --git a/tests/unittests/config/test_cc_apk_configure.py b/tests/unittests/config/test_cc_apk_configure.py
index 8854670..e9533a1 100644
--- a/tests/unittests/config/test_cc_apk_configure.py
+++ b/tests/unittests/config/test_cc_apk_configure.py
@@ -18,6 +18,7 @@ from cloudinit.config.schema import (
validate_cloudconfig_schema,
)
from tests.unittests.helpers import (
+ SCHEMA_EMPTY_ERROR,
FilesystemMockingTestCase,
mock,
skipUnlessJsonSchema,
@@ -355,7 +356,7 @@ class TestApkConfigureSchema:
(
{"apk_repos": {"alpine_repo": {}}},
"apk_repos.alpine_repo: 'version' is a required property,"
- " apk_repos.alpine_repo: {} does not have enough properties",
+ f" apk_repos.alpine_repo: {{}} {SCHEMA_EMPTY_ERROR}",
),
(
{"apk_repos": {"alpine_repo": True}},
@@ -368,7 +369,7 @@ class TestApkConfigureSchema:
),
(
{"apk_repos": {}},
- "apk_repos: {} does not have enough properties",
+ f"apk_repos: {{}} {SCHEMA_EMPTY_ERROR}",
),
(
{"apk_repos": {"local_repo_base_url": None}},
diff --git a/tests/unittests/config/test_cc_apt_configure.py b/tests/unittests/config/test_cc_apt_configure.py
index 18e6663..4fff316 100644
--- a/tests/unittests/config/test_cc_apt_configure.py
+++ b/tests/unittests/config/test_cc_apt_configure.py
@@ -12,7 +12,7 @@ from cloudinit.config.schema import (
get_schema,
validate_cloudconfig_schema,
)
-from tests.unittests.helpers import skipUnlessJsonSchema
+from tests.unittests.helpers import SCHEMA_EMPTY_ERROR, skipUnlessJsonSchema
from tests.unittests.util import get_cloud
@@ -34,7 +34,7 @@ class TestAPTConfigureSchema:
" ('boguskey' was unexpected)"
),
),
- ({"apt": {}}, "apt: {} does not have enough properties"),
+ ({"apt": {}}, f"apt: {{}} {SCHEMA_EMPTY_ERROR}"),
(
{"apt": {"preserve_sources_list": 1}},
"apt.preserve_sources_list: 1 is not of type 'boolean'",
@@ -45,7 +45,7 @@ class TestAPTConfigureSchema:
),
(
{"apt": {"disable_suites": []}},
- re.escape("apt.disable_suites: [] is too short"),
+ re.escape("apt.disable_suites: [] ") + SCHEMA_EMPTY_ERROR,
),
(
{"apt": {"disable_suites": [1]}},
@@ -65,7 +65,7 @@ class TestAPTConfigureSchema:
),
(
{"apt": {"primary": []}},
- re.escape("apt.primary: [] is too short"),
+ re.escape("apt.primary: [] ") + SCHEMA_EMPTY_ERROR,
),
(
{"apt": {"primary": ["nonobj"]}},
@@ -102,7 +102,7 @@ class TestAPTConfigureSchema:
),
(
{"apt": {"primary": [{"arches": ["amd64"], "search": []}]}},
- re.escape("apt.primary.0.search: [] is too short"),
+ re.escape("apt.primary.0.search: [] ") + SCHEMA_EMPTY_ERROR,
),
(
{
@@ -134,7 +134,7 @@ class TestAPTConfigureSchema:
),
(
{"apt": {"debconf_selections": {}}},
- "apt.debconf_selections: {} does not have enough properties",
+ f"apt.debconf_selections: {{}} {SCHEMA_EMPTY_ERROR}",
),
(
{"apt": {"sources_list": True}},
@@ -170,7 +170,7 @@ class TestAPTConfigureSchema:
),
(
{"apt": {"sources": {"opaquekey": {}}}},
- "apt.sources.opaquekey: {} does not have enough properties",
+ f"apt.sources.opaquekey: {{}} {SCHEMA_EMPTY_ERROR}",
),
(
{"apt": {"sources": {"opaquekey": {"boguskey": True}}}},
diff --git a/tests/unittests/config/test_cc_bootcmd.py b/tests/unittests/config/test_cc_bootcmd.py
index f6772df..b938732 100644
--- a/tests/unittests/config/test_cc_bootcmd.py
+++ b/tests/unittests/config/test_cc_bootcmd.py
@@ -11,7 +11,12 @@ from cloudinit.config.schema import (
get_schema,
validate_cloudconfig_schema,
)
-from tests.unittests.helpers import CiTestCase, mock, skipUnlessJsonSchema
+from tests.unittests.helpers import (
+ SCHEMA_EMPTY_ERROR,
+ CiTestCase,
+ mock,
+ skipUnlessJsonSchema,
+)
from tests.unittests.util import get_cloud
@@ -128,12 +133,14 @@ class TestBootCMDSchema:
"Cloud config schema errors: bootcmd: 1 is not of type"
" 'array'",
),
- ({"bootcmd": []}, re.escape("bootcmd: [] is too short")),
(
{"bootcmd": []},
- re.escape(
- "Cloud config schema errors: bootcmd: [] is too short"
- ),
+ re.escape("bootcmd: [] ") + SCHEMA_EMPTY_ERROR,
+ ),
+ (
+ {"bootcmd": []},
+ re.escape("Cloud config schema errors: bootcmd: [] ")
+ + SCHEMA_EMPTY_ERROR,
),
(
{
diff --git a/tests/unittests/config/test_cc_ca_certs.py b/tests/unittests/config/test_cc_ca_certs.py
index b93fda7..cb5712b 100644
--- a/tests/unittests/config/test_cc_ca_certs.py
+++ b/tests/unittests/config/test_cc_ca_certs.py
@@ -17,7 +17,11 @@ from cloudinit.config.schema import (
get_schema,
validate_cloudconfig_schema,
)
-from tests.unittests.helpers import TestCase, skipUnlessJsonSchema
+from tests.unittests.helpers import (
+ SCHEMA_EMPTY_ERROR,
+ TestCase,
+ skipUnlessJsonSchema,
+)
from tests.unittests.util import get_cloud
@@ -398,7 +402,7 @@ class TestCACertsSchema:
),
(
{"ca_certs": {}},
- re.escape("ca_certs: {} does not have enough properties"),
+ re.escape("ca_certs: {} ") + SCHEMA_EMPTY_ERROR,
),
(
{"ca_certs": {"boguskey": 1}},
@@ -417,7 +421,7 @@ class TestCACertsSchema:
),
(
{"ca_certs": {"trusted": []}},
- re.escape("ca_certs.trusted: [] is too short"),
+ re.escape("ca_certs.trusted: [] ") + SCHEMA_EMPTY_ERROR,
),
),
)
diff --git a/tests/unittests/config/test_cc_chef.py b/tests/unittests/config/test_cc_chef.py
index 9d8ba1f..6fad6a7 100644
--- a/tests/unittests/config/test_cc_chef.py
+++ b/tests/unittests/config/test_cc_chef.py
@@ -15,6 +15,7 @@ from cloudinit.config.schema import (
validate_cloudconfig_schema,
)
from tests.unittests.helpers import (
+ SCHEMA_EMPTY_ERROR,
FilesystemMockingTestCase,
ResponsesTestCase,
cloud_init_project_dir,
@@ -306,7 +307,7 @@ class TestBootCMDSchema:
),
(
{"chef": {}},
- re.escape(" chef: {} does not have enough properties"),
+ re.escape(" chef: {} ") + SCHEMA_EMPTY_ERROR,
),
(
{"chef": {"boguskey": True}},
@@ -321,7 +322,7 @@ class TestBootCMDSchema:
),
(
{"chef": {"directories": []}},
- re.escape("chef.directories: [] is too short"),
+ re.escape("chef.directories: [] ") + SCHEMA_EMPTY_ERROR,
),
(
{"chef": {"directories": [1]}},
diff --git a/tests/unittests/config/test_cc_lxd.py b/tests/unittests/config/test_cc_lxd.py
index 648fc33..320bfdb 100644
--- a/tests/unittests/config/test_cc_lxd.py
+++ b/tests/unittests/config/test_cc_lxd.py
@@ -385,7 +385,7 @@ class TestLXDSchema:
# Require bridge.mode
({"lxd": {"bridge": {}}}, "bridge: 'mode' is a required property"),
# Require init or bridge keys
- ({"lxd": {}}, "lxd: {} does not have enough properties"),
+ ({"lxd": {}}, f"lxd: {{}} {t_help.SCHEMA_EMPTY_ERROR}"),
# Require some non-empty preseed config of type string
({"lxd": {"preseed": {}}}, "not of type 'string'"),
({"lxd": {"preseed": ""}}, None),
diff --git a/tests/unittests/config/test_cc_mounts.py b/tests/unittests/config/test_cc_mounts.py
index a2dada8..4795357 100644
--- a/tests/unittests/config/test_cc_mounts.py
+++ b/tests/unittests/config/test_cc_mounts.py
@@ -583,9 +583,15 @@ class TestMountsSchema:
"config, error_msg",
[
# We expect to see one mount if provided in user-data.
- ({"mounts": []}, re.escape("mounts: [] is too short")),
+ (
+ {"mounts": []},
+ re.escape("mounts: [] ") + test_helpers.SCHEMA_EMPTY_ERROR,
+ ),
# Disallow less than 1 item per mount entry
- ({"mounts": [[]]}, re.escape("mounts.0: [] is too short")),
+ (
+ {"mounts": [[]]},
+ re.escape("mounts.0: [] ") + test_helpers.SCHEMA_EMPTY_ERROR,
+ ),
# Disallow more than 6 items per mount entry
({"mounts": [["1"] * 7]}, "mounts.0:.* is too long"),
# Disallow mount_default_fields will anything other than 6 items
diff --git a/tests/unittests/config/test_cc_package_update_upgrade_install.py b/tests/unittests/config/test_cc_package_update_upgrade_install.py
index 9ba7f17..b3d43a1 100644
--- a/tests/unittests/config/test_cc_package_update_upgrade_install.py
+++ b/tests/unittests/config/test_cc_package_update_upgrade_install.py
@@ -14,6 +14,7 @@ from cloudinit.config.schema import (
from cloudinit.distros import PackageInstallerError
from cloudinit.subp import SubpResult
from tests.unittests.helpers import skipUnlessJsonSchema
+from tests.unittests.helpers import SCHEMA_EMPTY_ERROR
from tests.unittests.util import get_cloud
@@ -188,7 +189,7 @@ class TestPackageUpdateUpgradeSchema:
# packages list with three entries (2 required)
({"packages": ["p1", ["p2", "p3", "p4"]]}, ""),
# empty packages list
- ({"packages": []}, "is too short"),
+ ({"packages": []}, SCHEMA_EMPTY_ERROR),
(
{"apt_update": False},
(
diff --git a/tests/unittests/config/test_cc_runcmd.py b/tests/unittests/config/test_cc_runcmd.py
index a8636bb..f69cd9d 100644
--- a/tests/unittests/config/test_cc_runcmd.py
+++ b/tests/unittests/config/test_cc_runcmd.py
@@ -14,6 +14,7 @@ from cloudinit.config.schema import (
validate_cloudconfig_schema,
)
from tests.unittests.helpers import (
+ SCHEMA_EMPTY_ERROR,
FilesystemMockingTestCase,
skipUnlessJsonSchema,
)
@@ -90,7 +91,7 @@ class TestRunCmdSchema:
({"runcmd": ["echo bye", "echo bye"]}, None),
# Invalid schemas
({"runcmd": 1}, "1 is not of type 'array'"),
- ({"runcmd": []}, r"runcmd: \[\] is too short"),
+ ({"runcmd": []}, rf"runcmd: \[\] {SCHEMA_EMPTY_ERROR}"),
(
{
"runcmd": [
diff --git a/tests/unittests/config/test_cc_set_passwords.py b/tests/unittests/config/test_cc_set_passwords.py
index 1a9fcd3..ef34a8c 100644
--- a/tests/unittests/config/test_cc_set_passwords.py
+++ b/tests/unittests/config/test_cc_set_passwords.py
@@ -12,7 +12,11 @@ from cloudinit.config.schema import (
get_schema,
validate_cloudconfig_schema,
)
-from tests.unittests.helpers import does_not_raise, skipUnlessJsonSchema
+from tests.unittests.helpers import (
+ SCHEMA_EMPTY_ERROR,
+ does_not_raise,
+ skipUnlessJsonSchema,
+)
from tests.unittests.util import get_cloud
MODPATH = "cloudinit.config.cc_set_passwords."
@@ -718,7 +722,8 @@ class TestSetPasswordsSchema:
(
{"chpasswd": {"list": []}},
pytest.raises(
- SchemaValidationError, match=r"\[\] is too short"
+ SchemaValidationError,
+ match=rf"\[\] {SCHEMA_EMPTY_ERROR}",
),
),
],
diff --git a/tests/unittests/config/test_cc_snap.py b/tests/unittests/config/test_cc_snap.py
index 65088dd..573ade9 100644
--- a/tests/unittests/config/test_cc_snap.py
+++ b/tests/unittests/config/test_cc_snap.py
@@ -14,7 +14,12 @@ from cloudinit.config.schema import (
get_schema,
validate_cloudconfig_schema,
)
-from tests.unittests.helpers import CiTestCase, mock, skipUnlessJsonSchema
+from tests.unittests.helpers import (
+ SCHEMA_EMPTY_ERROR,
+ CiTestCase,
+ mock,
+ skipUnlessJsonSchema,
+)
from tests.unittests.util import get_cloud
M_PATH = "cloudinit.config.cc_snap."
@@ -288,15 +293,18 @@ class TestSnapSchema:
{"snap": {"commands": ["ls"], "invalid-key": ""}},
"Additional properties are not allowed",
),
- ({"snap": {}}, "{} does not have enough properties"),
+ ({"snap": {}}, f"{{}} {SCHEMA_EMPTY_ERROR}"),
(
{"snap": {"commands": "broken"}},
"'broken' is not of type 'object', 'array'",
),
- ({"snap": {"commands": []}}, r"snap.commands: \[\] is too short"),
+ (
+ {"snap": {"commands": []}},
+ rf"snap.commands: \[\] {SCHEMA_EMPTY_ERROR}",
+ ),
(
{"snap": {"commands": {}}},
- r"snap.commands: {} does not have enough properties",
+ rf"snap.commands: {{}} {SCHEMA_EMPTY_ERROR}",
),
({"snap": {"commands": [123]}}, ""),
({"snap": {"commands": {"01": 123}}}, ""),
@@ -311,10 +319,10 @@ class TestSnapSchema:
{"snap": {"assertions": "broken"}},
"'broken' is not of type 'object', 'array'",
),
- ({"snap": {"assertions": []}}, r"\[\] is too short"),
+ ({"snap": {"assertions": []}}, rf"\[\] {SCHEMA_EMPTY_ERROR}"),
(
{"snap": {"assertions": {}}},
- r"\{} does not have enough properties",
+ rf"\{{}} {SCHEMA_EMPTY_ERROR}",
),
],
)
diff --git a/tests/unittests/config/test_cc_write_files.py b/tests/unittests/config/test_cc_write_files.py
index 210edf7..ea9cf8a 100644
--- a/tests/unittests/config/test_cc_write_files.py
+++ b/tests/unittests/config/test_cc_write_files.py
@@ -18,6 +18,7 @@ from cloudinit.config.schema import (
validate_cloudconfig_schema,
)
from tests.unittests.helpers import (
+ SCHEMA_EMPTY_ERROR,
CiTestCase,
FilesystemMockingTestCase,
skipUnlessJsonSchema,
@@ -222,7 +223,10 @@ class TestWriteFilesSchema:
[
# Top-level write_files type validation
({"write_files": 1}, "write_files: 1 is not of type 'array'"),
- ({"write_files": []}, re.escape("write_files: [] is too short")),
+ (
+ {"write_files": []},
+ re.escape("write_files: [] ") + SCHEMA_EMPTY_ERROR,
+ ),
(
{"write_files": [{}]},
"write_files.0: 'path' is a required property",
diff --git a/tests/unittests/config/test_cc_yum_add_repo.py b/tests/unittests/config/test_cc_yum_add_repo.py
index d2c2912..e7392f2 100644
--- a/tests/unittests/config/test_cc_yum_add_repo.py
+++ b/tests/unittests/config/test_cc_yum_add_repo.py
@@ -139,7 +139,7 @@ class TestAddYumRepoSchema:
),
(
{"yum_repos": {}},
- re.escape("yum_repos: {} does not have enough properties"),
+ re.escape("yum_repos: {} ") + helpers.SCHEMA_EMPTY_ERROR,
),
# baseurl required
(
diff --git a/tests/unittests/helpers.py b/tests/unittests/helpers.py
index 96f407f..42cc2e9 100644
--- a/tests/unittests/helpers.py
+++ b/tests/unittests/helpers.py
@@ -47,6 +47,14 @@ except ImportError:
HAS_APT_PKG = False
+# Used by tests to verify the error message when a jsonschema structure
+# is empty but should not be.
+# Version 4.20.0 of jsonschema changed the error messages for empty structures.
+SCHEMA_EMPTY_ERROR = (
+ "(is too short|should be non-empty|does not have enough properties)"
+)
+
+
# Makes the old path start
# with new base instead of whatever
# it previously had
diff --git a/tests/unittests/test_merging.py b/tests/unittests/test_merging.py
index 891031e..52abaa7 100644
--- a/tests/unittests/test_merging.py
+++ b/tests/unittests/test_merging.py
@@ -270,7 +270,7 @@ class TestMergingSchema:
[
({"merge_how": "list()+dict()+str()"}, None),
({"merge_type": "list()+dict()+str()"}, None),
- ({"merge_how": []}, "\\[\\] is too short"),
+ ({"merge_how": []}, f"\\[\\] {helpers.SCHEMA_EMPTY_ERROR}"),
(
{"merge_how": {"name": "list", "settings": ["append"]}},
"is not of type",
--
2.27.0

View File

@ -0,0 +1,72 @@
From dc0eafbc7d88be99e11301081adb41ad7b50338e Mon Sep 17 00:00:00 2001
From: James Falcon <james.falcon@canonical.com>
Date: Mon, 11 Mar 2024 19:37:48 -0500
Subject: [PATCH] test: fix `disable_sysfs_net` mock (#5065)
The fixture parametrization ability added in 9baf31c doesn't work
as expected. When you have a session-wide fixture, the setup is run
once, then further invocations of the fixture (including autouse) uses a
cached version of the fixture. Teardown for the session fixture happens
at the end of all test runs. This also applies to mock patching. Since
the mock patching happens only once, parametrizing the fixture to yield
without patching doesn't undo the initial mock setup; the
parametrization of `disable_sys_net` effectively does nothing.
The good news is that patches stack, so current tests that patch
`get_sys_class_path` differently will still work fine. If we need to
disable the patching entirely, that is also possible by saving the
original `get_sys_class_path` before applying the global disable mock,
then having a separate mock that has a side effect of calling
the original function.
Reference:https://github.com/canonical/cloud-init/commit/dc0eafbc7d88be99e11301081adb41ad7b50338e
Conflict:NA
---
tests/unittests/conftest.py | 14 ++------------
tests/unittests/net/test_init.py | 5 +----
2 files changed, 3 insertions(+), 16 deletions(-)
diff --git a/tests/unittests/conftest.py b/tests/unittests/conftest.py
index 22bc189..bdd21c3 100644
--- a/tests/unittests/conftest.py
+++ b/tests/unittests/conftest.py
@@ -62,18 +62,8 @@ def fake_filesystem(mocker, tmpdir):
@pytest.fixture(scope="session", autouse=True)
-def disable_sysfs_net(request, tmpdir_factory):
- """Avoid tests which read the undertying host's /syc/class/net.
-
- To allow unobscured reads of /sys/class/net on the host we can
- parametrize the fixture with:
-
- @pytest.mark.parametrize("disable_sysfs_net", [False], indirect=True)
- """
- if hasattr(request, "param") and getattr(request, "param") is False:
- # Test disabled this fixture, perform no mocks.
- yield
- return
+def disable_sysfs_net(tmpdir_factory):
+ """Avoid tests which read the underlying host's /syc/class/net."""
mock_sysfs = f"{tmpdir_factory.mktemp('sysfs')}/"
with mock.patch(
"cloudinit.net.get_sys_class_path", return_value=mock_sysfs
diff --git a/tests/unittests/net/test_init.py b/tests/unittests/net/test_init.py
index a7b75ab..51e54d0 100644
--- a/tests/unittests/net/test_init.py
+++ b/tests/unittests/net/test_init.py
@@ -42,10 +42,7 @@ class TestSysDevPath:
class TestReadSysNet:
@pytest.fixture(autouse=True)
- @pytest.mark.parametrize(
- "disable_sysfs_net", [False], indirect=["disable_sysfs_net"]
- )
- def setup(self, disable_sysfs_net, tmpdir_factory):
+ def setup(self, tmpdir_factory):
# We mock invididual numbered tmpdirs here because these tests write
# to the sysfs directory and stale test artifacts break later tests.
mock_sysfs = f"{tmpdir_factory.mktemp('sysfs', numbered=True)}/"
--
2.43.0

View File

@ -0,0 +1,37 @@
From 4ed78b116774dcbf78cfc556c823bdb7f8384068 Mon Sep 17 00:00:00 2001
From: James Falcon <james.falcon@canonical.com>
Date: Tue, 20 Feb 2024 11:08:13 -0600
Subject: [PATCH] test: fix tmpdir in test_cc_apk_configure (#4914)
cc_apk_configure uses temp_utils.py, which has special logic to return
if the user is root that can't be retargeted using our current fixtures.
Fix it by setting that tmpdir in the test setup.
---
tests/unittests/config/test_cc_apk_configure.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/tests/unittests/config/test_cc_apk_configure.py b/tests/unittests/config/test_cc_apk_configure.py
index 72ed82242ee..47777b4701a 100644
--- a/tests/unittests/config/test_cc_apk_configure.py
+++ b/tests/unittests/config/test_cc_apk_configure.py
@@ -10,7 +10,7 @@
import pytest
-from cloudinit import cloud, helpers, util
+from cloudinit import cloud, helpers, temp_utils, util
from cloudinit.config import cc_apk_configure
from cloudinit.config.schema import (
SchemaValidationError,
@@ -60,6 +60,11 @@ def setUp(self):
self.name = "apk_configure"
self.cloud = cloud.Cloud(None, self.paths, None, None, None)
self.args = []
+ temp_utils._TMPDIR = self.new_root
+
+ def tearDown(self):
+ super().tearDown()
+ temp_utils._TMPDIR = None
@mock.patch(CC_APK + "._write_repositories_file")
def test_no_repo_settings(self, m_write_repos):

View File

@ -0,0 +1,672 @@
From 9baf31c7108b031d469e6476b949e042aba249ea Mon Sep 17 00:00:00 2001
From: Chad Smith <chad.smith@canonical.com>
Date: Wed, 6 Mar 2024 09:47:36 -0700
Subject: [PATCH] tests: drop CiTestCase and convert to pytest
Prevent disable_sysfs_net fixture from impacting TestSysDevPath
and TestReadSysNet in order to avoid shared mocks of /sys/class/net.
This avoid test artifact pollution for TestReadSysNet.
Adapt the following tests, dropping CiTestCase to use pytest:
TestDHCPDiscoveryClean, TestSysDevPath, TestReadSysNet,
TestGenerateFallbackConfig, TestNetFailOver, TestConvertNetJson
Fixes GH-4973
Reference:https://github.com/canonical/cloud-init/commit/9baf31c7108b031d469e6476b949e042aba249ea
Confilct:don't change test_dhcp.py, otherwise, it will introduce many test case failures (eg.
test_provided_nic_does_not_exist). Some failed tests will be deleted in
21b2b6e4423b0fec325b32042c05ef4274cdd301.
---
tests/unittests/net/test_init.py | 298 +++++++++---------
.../sources/helpers/test_openstack.py | 17 +-
2 files changed, 149 insertions(+), 166 deletions(-)
diff --git a/tests/unittests/net/test_init.py b/tests/unittests/net/test_init.py
index 561d515..a7b75ab 100644
--- a/tests/unittests/net/test_init.py
+++ b/tests/unittests/net/test_init.py
@@ -17,49 +17,55 @@ from cloudinit import subp
from cloudinit.net.ephemeral import EphemeralIPv4Network, EphemeralIPv6Network
from cloudinit.subp import ProcessExecutionError
from cloudinit.util import ensure_file, write_file
-from tests.unittests.helpers import CiTestCase, ResponsesTestCase
+from tests.unittests.helpers import (
+ CiTestCase,
+ ResponsesTestCase,
+ random_string,
+)
from tests.unittests.util import MockDistro
-class TestSysDevPath(CiTestCase):
+class TestSysDevPath:
def test_sys_dev_path(self):
"""sys_dev_path returns a path under SYS_CLASS_NET for a device."""
dev = "something"
path = "attribute"
- expected = net.SYS_CLASS_NET + dev + "/" + path
- self.assertEqual(expected, net.sys_dev_path(dev, path))
+ expected = net.get_sys_class_path() + dev + "/" + path
+ assert expected == net.sys_dev_path(dev, path)
def test_sys_dev_path_without_path(self):
"""When path param isn't provided it defaults to empty string."""
dev = "something"
- expected = net.SYS_CLASS_NET + dev + "/"
- self.assertEqual(expected, net.sys_dev_path(dev))
-
+ expected = net.get_sys_class_path() + dev + "/"
+ assert expected == net.sys_dev_path(dev)
-class TestReadSysNet(CiTestCase):
- with_logs = True
- def setUp(self):
- super(TestReadSysNet, self).setUp()
- sys_mock = mock.patch("cloudinit.net.get_sys_class_path")
- self.m_sys_path = sys_mock.start()
- self.sysdir = self.tmp_dir() + "/"
- self.m_sys_path.return_value = self.sysdir
- self.addCleanup(sys_mock.stop)
+class TestReadSysNet:
+ @pytest.fixture(autouse=True)
+ @pytest.mark.parametrize(
+ "disable_sysfs_net", [False], indirect=["disable_sysfs_net"]
+ )
+ def setup(self, disable_sysfs_net, tmpdir_factory):
+ # We mock invididual numbered tmpdirs here because these tests write
+ # to the sysfs directory and stale test artifacts break later tests.
+ mock_sysfs = f"{tmpdir_factory.mktemp('sysfs', numbered=True)}/"
+ with mock.patch(
+ "cloudinit.net.get_sys_class_path", return_value=mock_sysfs
+ ):
+ self.sysdir = mock_sysfs
+ yield
def test_read_sys_net_strips_contents_of_sys_path(self):
"""read_sys_net strips whitespace from the contents of a sys file."""
content = "some stuff with trailing whitespace\t\r\n"
write_file(os.path.join(self.sysdir, "dev", "attr"), content)
- self.assertEqual(content.strip(), net.read_sys_net("dev", "attr"))
+ assert content.strip() == net.read_sys_net("dev", "attr")
def test_read_sys_net_reraises_oserror(self):
"""read_sys_net raises OSError/IOError when file doesn't exist."""
# Non-specific Exception because versions of python OSError vs IOError.
- with self.assertRaises(Exception) as context_manager: # noqa: H202
+ with pytest.raises(Exception, match="No such file or directory"):
net.read_sys_net("dev", "attr")
- error = context_manager.exception
- self.assertIn("No such file or directory", str(error))
def test_read_sys_net_handles_error_with_on_enoent(self):
"""read_sys_net handles OSError/IOError with on_enoent if provided."""
@@ -70,30 +76,27 @@ class TestReadSysNet(CiTestCase):
net.read_sys_net("dev", "attr", on_enoent=on_enoent)
error = handled_errors[0]
- self.assertIsInstance(error, Exception)
- self.assertIn("No such file or directory", str(error))
+ assert isinstance(error, Exception)
+ assert "No such file or directory" in str(error)
def test_read_sys_net_translates_content(self):
"""read_sys_net translates content when translate dict is provided."""
content = "you're welcome\n"
write_file(os.path.join(self.sysdir, "dev", "attr"), content)
translate = {"you're welcome": "de nada"}
- self.assertEqual(
- "de nada", net.read_sys_net("dev", "attr", translate=translate)
+ assert "de nada" == net.read_sys_net(
+ "dev", "attr", translate=translate
)
- def test_read_sys_net_errors_on_translation_failures(self):
+ def test_read_sys_net_errors_on_translation_failures(self, caplog):
"""read_sys_net raises a KeyError and logs details on failure."""
content = "you're welcome\n"
write_file(os.path.join(self.sysdir, "dev", "attr"), content)
- with self.assertRaises(KeyError) as context_manager:
+ with pytest.raises(KeyError, match='"you\'re welcome"'):
net.read_sys_net("dev", "attr", translate={})
- error = context_manager.exception
- self.assertEqual('"you\'re welcome"', str(error))
- self.assertIn(
+ assert (
"Found unexpected (not translatable) value 'you're welcome' in "
- "'{0}dev/attr".format(self.sysdir),
- self.logs.getvalue(),
+ "'{0}dev/attr".format(self.sysdir) in caplog.text
)
def test_read_sys_net_handles_handles_with_onkeyerror(self):
@@ -107,63 +110,63 @@ class TestReadSysNet(CiTestCase):
net.read_sys_net("dev", "attr", translate={}, on_keyerror=on_keyerror)
error = handled_errors[0]
- self.assertIsInstance(error, KeyError)
- self.assertEqual('"you\'re welcome"', str(error))
+ assert isinstance(error, KeyError)
+ assert '"you\'re welcome"' == str(error)
def test_read_sys_net_safe_false_on_translate_failure(self):
"""read_sys_net_safe returns False on translation failures."""
content = "you're welcome\n"
write_file(os.path.join(self.sysdir, "dev", "attr"), content)
- self.assertFalse(net.read_sys_net_safe("dev", "attr", translate={}))
+ assert not net.read_sys_net_safe("dev", "attr", translate={})
def test_read_sys_net_safe_returns_false_on_noent_failure(self):
"""read_sys_net_safe returns False on file not found failures."""
- self.assertFalse(net.read_sys_net_safe("dev", "attr"))
+ assert not net.read_sys_net_safe("dev", "attr")
def test_read_sys_net_int_returns_none_on_error(self):
"""read_sys_net_safe returns None on failures."""
- self.assertFalse(net.read_sys_net_int("dev", "attr"))
+ assert not net.read_sys_net_int("dev", "attr")
def test_read_sys_net_int_returns_none_on_valueerror(self):
"""read_sys_net_safe returns None when content is not an int."""
write_file(os.path.join(self.sysdir, "dev", "attr"), "NOTINT\n")
- self.assertFalse(net.read_sys_net_int("dev", "attr"))
+ assert not net.read_sys_net_int("dev", "attr")
def test_read_sys_net_int_returns_integer_from_content(self):
"""read_sys_net_safe returns None on failures."""
write_file(os.path.join(self.sysdir, "dev", "attr"), "1\n")
- self.assertEqual(1, net.read_sys_net_int("dev", "attr"))
+ assert 1 == net.read_sys_net_int("dev", "attr")
def test_is_up_true(self):
"""is_up is True if sys/net/devname/operstate is 'up' or 'unknown'."""
for state in ["up", "unknown"]:
write_file(os.path.join(self.sysdir, "eth0", "operstate"), state)
- self.assertTrue(net.is_up("eth0"))
+ assert net.is_up("eth0")
def test_is_up_false(self):
"""is_up is False if sys/net/devname/operstate is 'down' or invalid."""
for state in ["down", "incomprehensible"]:
write_file(os.path.join(self.sysdir, "eth0", "operstate"), state)
- self.assertFalse(net.is_up("eth0"))
+ assert not net.is_up("eth0")
def test_is_bridge(self):
"""is_bridge is True when /sys/net/devname/bridge exists."""
- self.assertFalse(net.is_bridge("eth0"))
+ assert not net.is_bridge("eth0")
ensure_file(os.path.join(self.sysdir, "eth0", "bridge"))
- self.assertTrue(net.is_bridge("eth0"))
+ assert net.is_bridge("eth0")
def test_is_bond(self):
"""is_bond is True when /sys/net/devname/bonding exists."""
- self.assertFalse(net.is_bond("eth0"))
+ assert not net.is_bond("eth0")
ensure_file(os.path.join(self.sysdir, "eth0", "bonding"))
- self.assertTrue(net.is_bond("eth0"))
+ assert net.is_bond("eth0")
def test_get_master(self):
"""get_master returns the path when /sys/net/devname/master exists."""
- self.assertIsNone(net.get_master("enP1s1"))
+ assert net.get_master("enP1s1") is None
master_path = os.path.join(self.sysdir, "enP1s1", "master")
ensure_file(master_path)
- self.assertEqual(master_path, net.get_master("enP1s1"))
+ assert master_path == net.get_master("enP1s1")
def test_master_is_bridge_or_bond(self):
bridge_mac = "aa:bb:cc:aa:bb:cc"
@@ -173,8 +176,8 @@ class TestReadSysNet(CiTestCase):
write_file(os.path.join(self.sysdir, "eth1", "address"), bridge_mac)
write_file(os.path.join(self.sysdir, "eth2", "address"), bond_mac)
- self.assertFalse(net.master_is_bridge_or_bond("eth1"))
- self.assertFalse(net.master_is_bridge_or_bond("eth2"))
+ assert not net.master_is_bridge_or_bond("eth1")
+ assert not net.master_is_bridge_or_bond("eth2")
# masters without bridge/bonding => False
write_file(os.path.join(self.sysdir, "br0", "address"), bridge_mac)
@@ -183,15 +186,15 @@ class TestReadSysNet(CiTestCase):
os.symlink("../br0", os.path.join(self.sysdir, "eth1", "master"))
os.symlink("../bond0", os.path.join(self.sysdir, "eth2", "master"))
- self.assertFalse(net.master_is_bridge_or_bond("eth1"))
- self.assertFalse(net.master_is_bridge_or_bond("eth2"))
+ assert not net.master_is_bridge_or_bond("eth1")
+ assert not net.master_is_bridge_or_bond("eth2")
# masters with bridge/bonding => True
write_file(os.path.join(self.sysdir, "br0", "bridge"), "")
write_file(os.path.join(self.sysdir, "bond0", "bonding"), "")
- self.assertTrue(net.master_is_bridge_or_bond("eth1"))
- self.assertTrue(net.master_is_bridge_or_bond("eth2"))
+ assert net.master_is_bridge_or_bond("eth1")
+ assert net.master_is_bridge_or_bond("eth2")
def test_master_is_openvswitch(self):
ovs_mac = "bb:cc:aa:bb:cc:aa"
@@ -199,7 +202,7 @@ class TestReadSysNet(CiTestCase):
# No master => False
write_file(os.path.join(self.sysdir, "eth1", "address"), ovs_mac)
- self.assertFalse(net.master_is_bridge_or_bond("eth1"))
+ assert not net.master_is_bridge_or_bond("eth1")
# masters without ovs-system => False
write_file(os.path.join(self.sysdir, "ovs-system", "address"), ovs_mac)
@@ -208,7 +211,7 @@ class TestReadSysNet(CiTestCase):
"../ovs-system", os.path.join(self.sysdir, "eth1", "master")
)
- self.assertFalse(net.master_is_openvswitch("eth1"))
+ assert not net.master_is_openvswitch("eth1")
# masters with ovs-system => True
os.symlink(
@@ -216,15 +219,15 @@ class TestReadSysNet(CiTestCase):
os.path.join(self.sysdir, "eth1", "upper_ovs-system"),
)
- self.assertTrue(net.master_is_openvswitch("eth1"))
+ assert net.master_is_openvswitch("eth1")
def test_is_vlan(self):
"""is_vlan is True when /sys/net/devname/uevent has DEVTYPE=vlan."""
ensure_file(os.path.join(self.sysdir, "eth0", "uevent"))
- self.assertFalse(net.is_vlan("eth0"))
+ assert not net.is_vlan("eth0")
content = "junk\nDEVTYPE=vlan\njunk\n"
write_file(os.path.join(self.sysdir, "eth0", "uevent"), content)
- self.assertTrue(net.is_vlan("eth0"))
+ assert net.is_vlan("eth0")
class TestGenerateFallbackConfig(CiTestCase):
@@ -1453,134 +1456,121 @@ class TestExtractPhysdevs(CiTestCase):
net.extract_physdevs({"version": 3, "awesome_config": []})
-class TestNetFailOver(CiTestCase):
- def setUp(self):
- super(TestNetFailOver, self).setUp()
- self.add_patch("cloudinit.net.util", "m_util")
- self.add_patch("cloudinit.net.read_sys_net", "m_read_sys_net")
- self.add_patch("cloudinit.net.device_driver", "m_device_driver")
+class TestNetFailOver:
+ @pytest.fixture(autouse=True)
+ def setup(self, mocker):
+ mocker.patch("cloudinit.net.util")
+ self.device_driver = mocker.patch("cloudinit.net.device_driver")
+ self.read_sys_net = mocker.patch("cloudinit.net.read_sys_net")
def test_get_dev_features(self):
- devname = self.random_string()
- features = self.random_string()
- self.m_read_sys_net.return_value = features
+ devname = random_string()
+ features = random_string()
+ self.read_sys_net.return_value = features
- self.assertEqual(features, net.get_dev_features(devname))
- self.assertEqual(1, self.m_read_sys_net.call_count)
- self.assertEqual(
- mock.call(devname, "device/features"),
- self.m_read_sys_net.call_args_list[0],
- )
+ assert features == net.get_dev_features(devname)
+ assert 1 == self.read_sys_net.call_count
+ self.read_sys_net.assert_called_once_with(devname, "device/features")
def test_get_dev_features_none_returns_empty_string(self):
- devname = self.random_string()
- self.m_read_sys_net.side_effect = Exception("error")
- self.assertEqual("", net.get_dev_features(devname))
- self.assertEqual(1, self.m_read_sys_net.call_count)
- self.assertEqual(
- mock.call(devname, "device/features"),
- self.m_read_sys_net.call_args_list[0],
- )
+ devname = random_string()
+ self.read_sys_net.side_effect = Exception("error")
+ assert "" == net.get_dev_features(devname)
+ assert 1 == self.read_sys_net.call_count
+ self.read_sys_net.assert_called_once_with(devname, "device/features")
@mock.patch("cloudinit.net.get_dev_features")
def test_has_netfail_standby_feature(self, m_dev_features):
- devname = self.random_string()
+ devname = random_string()
standby_features = ("0" * 62) + "1" + "0"
m_dev_features.return_value = standby_features
- self.assertTrue(net.has_netfail_standby_feature(devname))
+ assert net.has_netfail_standby_feature(devname)
@mock.patch("cloudinit.net.get_dev_features")
def test_has_netfail_standby_feature_short_is_false(self, m_dev_features):
- devname = self.random_string()
- standby_features = self.random_string()
+ devname = random_string()
+ standby_features = random_string()
m_dev_features.return_value = standby_features
- self.assertFalse(net.has_netfail_standby_feature(devname))
+ assert not net.has_netfail_standby_feature(devname)
@mock.patch("cloudinit.net.get_dev_features")
def test_has_netfail_standby_feature_not_present_is_false(
self, m_dev_features
):
- devname = self.random_string()
+ devname = random_string()
standby_features = "0" * 64
m_dev_features.return_value = standby_features
- self.assertFalse(net.has_netfail_standby_feature(devname))
+ assert not net.has_netfail_standby_feature(devname)
@mock.patch("cloudinit.net.get_dev_features")
def test_has_netfail_standby_feature_no_features_is_false(
self, m_dev_features
):
- devname = self.random_string()
+ devname = random_string()
standby_features = None
m_dev_features.return_value = standby_features
- self.assertFalse(net.has_netfail_standby_feature(devname))
+ assert not net.has_netfail_standby_feature(devname)
@mock.patch("cloudinit.net.has_netfail_standby_feature")
@mock.patch("cloudinit.net.os.path.exists")
def test_is_netfail_master(self, m_exists, m_standby):
- devname = self.random_string()
+ devname = random_string()
driver = "virtio_net"
m_exists.return_value = False # no master sysfs attr
m_standby.return_value = True # has standby feature flag
- self.assertTrue(net.is_netfail_master(devname, driver))
+ assert net.is_netfail_master(devname, driver)
@mock.patch("cloudinit.net.sys_dev_path")
def test_is_netfail_master_checks_master_attr(self, m_sysdev):
- devname = self.random_string()
+ devname = random_string()
driver = "virtio_net"
- m_sysdev.return_value = self.random_string()
- self.assertFalse(net.is_netfail_master(devname, driver))
- self.assertEqual(1, m_sysdev.call_count)
- self.assertEqual(
- mock.call(devname, path="master"), m_sysdev.call_args_list[0]
- )
+ m_sysdev.return_value = random_string()
+ assert not net.is_netfail_master(devname, driver)
+ assert 1 == m_sysdev.call_count
+ m_sysdev.assert_called_once_with(devname, path="master")
@mock.patch("cloudinit.net.has_netfail_standby_feature")
@mock.patch("cloudinit.net.os.path.exists")
def test_is_netfail_master_wrong_driver(self, m_exists, m_standby):
- devname = self.random_string()
- driver = self.random_string()
- self.assertFalse(net.is_netfail_master(devname, driver))
+ devname = random_string()
+ driver = random_string()
+ assert not net.is_netfail_master(devname, driver)
@mock.patch("cloudinit.net.has_netfail_standby_feature")
@mock.patch("cloudinit.net.os.path.exists")
def test_is_netfail_master_has_master_attr(self, m_exists, m_standby):
- devname = self.random_string()
+ devname = random_string()
driver = "virtio_net"
m_exists.return_value = True # has master sysfs attr
- self.assertFalse(net.is_netfail_master(devname, driver))
+ assert not net.is_netfail_master(devname, driver)
@mock.patch("cloudinit.net.has_netfail_standby_feature")
@mock.patch("cloudinit.net.os.path.exists")
def test_is_netfail_master_no_standby_feat(self, m_exists, m_standby):
- devname = self.random_string()
+ devname = random_string()
driver = "virtio_net"
m_exists.return_value = False # no master sysfs attr
m_standby.return_value = False # no standby feature flag
- self.assertFalse(net.is_netfail_master(devname, driver))
+ assert not net.is_netfail_master(devname, driver)
@mock.patch("cloudinit.net.has_netfail_standby_feature")
@mock.patch("cloudinit.net.os.path.exists")
@mock.patch("cloudinit.net.sys_dev_path")
def test_is_netfail_primary(self, m_sysdev, m_exists, m_standby):
- devname = self.random_string()
- driver = self.random_string() # device not virtio_net
- master_devname = self.random_string()
+ devname = random_string()
+ driver = random_string() # device not virtio_net
+ master_devname = random_string()
m_sysdev.return_value = "%s/%s" % (
- self.random_string(),
+ random_string(),
master_devname,
)
m_exists.return_value = True # has master sysfs attr
- self.m_device_driver.return_value = "virtio_net" # master virtio_net
+ self.device_driver.return_value = "virtio_net" # master virtio_net
m_standby.return_value = True # has standby feature flag
- self.assertTrue(net.is_netfail_primary(devname, driver))
- self.assertEqual(1, self.m_device_driver.call_count)
- self.assertEqual(
- mock.call(master_devname), self.m_device_driver.call_args_list[0]
- )
- self.assertEqual(1, m_standby.call_count)
- self.assertEqual(
- mock.call(master_devname), m_standby.call_args_list[0]
- )
+ assert net.is_netfail_primary(devname, driver)
+ self.device_driver.assert_called_once_with(master_devname)
+ assert 1 == m_standby.call_count
+ m_standby.assert_called_once_with(master_devname)
@mock.patch("cloudinit.net.has_netfail_standby_feature")
@mock.patch("cloudinit.net.os.path.exists")
@@ -1588,18 +1578,18 @@ class TestNetFailOver(CiTestCase):
def test_is_netfail_primary_wrong_driver(
self, m_sysdev, m_exists, m_standby
):
- devname = self.random_string()
+ devname = random_string()
driver = "virtio_net"
- self.assertFalse(net.is_netfail_primary(devname, driver))
+ assert not net.is_netfail_primary(devname, driver)
@mock.patch("cloudinit.net.has_netfail_standby_feature")
@mock.patch("cloudinit.net.os.path.exists")
@mock.patch("cloudinit.net.sys_dev_path")
def test_is_netfail_primary_no_master(self, m_sysdev, m_exists, m_standby):
- devname = self.random_string()
- driver = self.random_string() # device not virtio_net
+ devname = random_string()
+ driver = random_string() # device not virtio_net
m_exists.return_value = False # no master sysfs attr
- self.assertFalse(net.is_netfail_primary(devname, driver))
+ assert not net.is_netfail_primary(devname, driver)
@mock.patch("cloudinit.net.has_netfail_standby_feature")
@mock.patch("cloudinit.net.os.path.exists")
@@ -1607,16 +1597,16 @@ class TestNetFailOver(CiTestCase):
def test_is_netfail_primary_bad_master(
self, m_sysdev, m_exists, m_standby
):
- devname = self.random_string()
- driver = self.random_string() # device not virtio_net
- master_devname = self.random_string()
+ devname = random_string()
+ driver = random_string() # device not virtio_net
+ master_devname = random_string()
m_sysdev.return_value = "%s/%s" % (
- self.random_string(),
+ random_string(),
master_devname,
)
m_exists.return_value = True # has master sysfs attr
- self.m_device_driver.return_value = "XXXX" # master not virtio_net
- self.assertFalse(net.is_netfail_primary(devname, driver))
+ self.device_driver.return_value = "XXXX" # master not virtio_net
+ assert not net.is_netfail_primary(devname, driver)
@mock.patch("cloudinit.net.has_netfail_standby_feature")
@mock.patch("cloudinit.net.os.path.exists")
@@ -1624,77 +1614,77 @@ class TestNetFailOver(CiTestCase):
def test_is_netfail_primary_no_standby(
self, m_sysdev, m_exists, m_standby
):
- devname = self.random_string()
- driver = self.random_string() # device not virtio_net
- master_devname = self.random_string()
+ devname = random_string()
+ driver = random_string() # device not virtio_net
+ master_devname = random_string()
m_sysdev.return_value = "%s/%s" % (
- self.random_string(),
+ random_string(),
master_devname,
)
m_exists.return_value = True # has master sysfs attr
- self.m_device_driver.return_value = "virtio_net" # master virtio_net
+ self.device_driver.return_value = "virtio_net" # master virtio_net
m_standby.return_value = False # master has no standby feature flag
- self.assertFalse(net.is_netfail_primary(devname, driver))
+ assert not net.is_netfail_primary(devname, driver)
@mock.patch("cloudinit.net.has_netfail_standby_feature")
@mock.patch("cloudinit.net.os.path.exists")
def test_is_netfail_standby(self, m_exists, m_standby):
- devname = self.random_string()
+ devname = random_string()
driver = "virtio_net"
m_exists.return_value = True # has master sysfs attr
m_standby.return_value = True # has standby feature flag
- self.assertTrue(net.is_netfail_standby(devname, driver))
+ assert net.is_netfail_standby(devname, driver)
@mock.patch("cloudinit.net.has_netfail_standby_feature")
@mock.patch("cloudinit.net.os.path.exists")
def test_is_netfail_standby_wrong_driver(self, m_exists, m_standby):
- devname = self.random_string()
- driver = self.random_string()
- self.assertFalse(net.is_netfail_standby(devname, driver))
+ devname = random_string()
+ driver = random_string()
+ assert not net.is_netfail_standby(devname, driver)
@mock.patch("cloudinit.net.has_netfail_standby_feature")
@mock.patch("cloudinit.net.os.path.exists")
def test_is_netfail_standby_no_master(self, m_exists, m_standby):
- devname = self.random_string()
+ devname = random_string()
driver = "virtio_net"
m_exists.return_value = False # has master sysfs attr
- self.assertFalse(net.is_netfail_standby(devname, driver))
+ assert not net.is_netfail_standby(devname, driver)
@mock.patch("cloudinit.net.has_netfail_standby_feature")
@mock.patch("cloudinit.net.os.path.exists")
def test_is_netfail_standby_no_standby_feature(self, m_exists, m_standby):
- devname = self.random_string()
+ devname = random_string()
driver = "virtio_net"
m_exists.return_value = True # has master sysfs attr
m_standby.return_value = False # has standby feature flag
- self.assertFalse(net.is_netfail_standby(devname, driver))
+ assert not net.is_netfail_standby(devname, driver)
@mock.patch("cloudinit.net.is_netfail_standby")
@mock.patch("cloudinit.net.is_netfail_primary")
def test_is_netfailover_primary(self, m_primary, m_standby):
- devname = self.random_string()
- driver = self.random_string()
+ devname = random_string()
+ driver = random_string()
m_primary.return_value = True
m_standby.return_value = False
- self.assertTrue(net.is_netfailover(devname, driver))
+ assert net.is_netfailover(devname, driver)
@mock.patch("cloudinit.net.is_netfail_standby")
@mock.patch("cloudinit.net.is_netfail_primary")
def test_is_netfailover_standby(self, m_primary, m_standby):
- devname = self.random_string()
- driver = self.random_string()
+ devname = random_string()
+ driver = random_string()
m_primary.return_value = False
m_standby.return_value = True
- self.assertTrue(net.is_netfailover(devname, driver))
+ assert net.is_netfailover(devname, driver)
@mock.patch("cloudinit.net.is_netfail_standby")
@mock.patch("cloudinit.net.is_netfail_primary")
def test_is_netfailover_returns_false(self, m_primary, m_standby):
- devname = self.random_string()
- driver = self.random_string()
+ devname = random_string()
+ driver = random_string()
m_primary.return_value = False
m_standby.return_value = False
- self.assertFalse(net.is_netfailover(devname, driver))
+ assert not net.is_netfailover(devname, driver)
class TestOpenvswitchIsInstalled:
diff --git a/tests/unittests/sources/helpers/test_openstack.py b/tests/unittests/sources/helpers/test_openstack.py
index ac8e2a3..4d85ec3 100644
--- a/tests/unittests/sources/helpers/test_openstack.py
+++ b/tests/unittests/sources/helpers/test_openstack.py
@@ -3,14 +3,13 @@
from unittest import mock
from cloudinit.sources.helpers import openstack
-from tests.unittests import helpers as test_helpers
@mock.patch(
"cloudinit.net.is_openvswitch_internal_interface",
mock.Mock(return_value=False),
)
-class TestConvertNetJson(test_helpers.CiTestCase):
+class TestConvertNetJson:
def test_phy_types(self):
"""Verify the different known physical types are handled."""
# network_data.json example from
@@ -54,11 +53,8 @@ class TestConvertNetJson(test_helpers.CiTestCase):
for t in openstack.KNOWN_PHYSICAL_TYPES:
net_json["links"][0]["type"] = t
- self.assertEqual(
- expected,
- openstack.convert_net_json(
- network_json=net_json, known_macs=macs
- ),
+ assert expected == openstack.convert_net_json(
+ network_json=net_json, known_macs=macs
)
def test_subnet_dns(self):
@@ -113,9 +109,6 @@ class TestConvertNetJson(test_helpers.CiTestCase):
for t in openstack.KNOWN_PHYSICAL_TYPES:
net_json["links"][0]["type"] = t
- self.assertEqual(
- expected,
- openstack.convert_net_json(
- network_json=net_json, known_macs=macs
- ),
+ assert expected == openstack.convert_net_json(
+ network_json=net_json, known_macs=macs
)
--
2.43.0

View File

@ -1,6 +1,6 @@
Name: cloud-init
Version: 23.4.1
Release: 1
Release: 2
Summary: the defacto multi-distribution package that handles early initialization of a cloud instance.
License: ASL 2.0 or GPLv3
URL: http://launchpad.net/cloud-init
@ -14,13 +14,19 @@ Patch3: add-variable-to-forbid-tmp-dir.patch
Patch5: Do-not-write-NM_CONTROLLED-no-in-generated-interface-config.patch
Patch6: delete-config-nopasswd-all.patch
Patch6000: backport-fix-unpin-jsonschema-and-update-tests.patch
Patch6001: backport-test-fix-tmpdir-in-test_cc_apk_configure.patch
Patch6002: backport-bug-tests-mock-reads-of-host-s-sys-class-net-via-get.patch
Patch6003: backport-tests-drop-CiTestCase-and-convert-to-pytest.patch
Patch6004: backport-test-fix-disable_sysfs_net-mock.patch
BuildRequires: pkgconfig(systemd) python3-devel python3-setuptools systemd
BuildRequires: iproute python3-configobj python3-responses
BuildRequires: python3-jinja2 python3-jsonpatch python3-jsonschema
BuildRequires: python3-mock python3-oauthlib python3-prettytable
BuildRequires: python3-pyserial python3-PyYAML python3-requests
BuildRequires: dnf %{_vendor}-release python3-pytest passwd python3-netifaces
BuildRequires: python3-pytest-mock
BuildRequires: python3-pytest-mock python3-passlib
Requires: e2fsprogs iproute python3-libselinux net-tools python3-policycoreutils
Requires: procps python3-configobj python3-jinja2 python3-jsonpatch xfsprogs
@ -55,7 +61,31 @@ install -D -m 0644 %{SOURCE1} %{buildroot}/%{_tmpfilesdir}/%{name}.conf
install -D -m 0644 tools/21-cloudinit.conf %{buildroot}/%{_sysconfdir}/rsyslog.d/21-cloudinit.conf
%check
python3 -m pytest tests/unittests/
SKIP_TESTS=""
# 检测是否存在多个网卡的MAC地址是ee:ee:ee:ee:ee:ee
# https://docs.tigera.io/calico/latest/reference/faq#why-do-all-cali-interfaces-have-the-mac-address-eeeeeeeeeeee
MAC_ADDR="ee:ee:ee:ee:ee:ee"
interfaces=$(ls /sys/class/net)
duplicate_mac_matched_count=0
for iface in $interfaces; do
if [ -e "/sys/class/net/$iface/address" ]; then
iface_mac=$(cat /sys/class/net/$iface/address)
if [ "$iface_mac" == "$MAC_ADDR" ]; then
duplicate_mac_matched_count=$((duplicate_mac_matched_count+1))
fi
fi
done
if [ "$duplicate_mac_matched_count" -gt 1 ]; then
SKIP_TESTS="not test_dhcp.py and not test_network_state.py"
fi
if [ -n "$SKIP_TESTS" ]; then
python3 -m pytest tests/unittests/ -k "$SKIP_TESTS"
else
python3 -m pytest tests/unittests/
fi
%pre
@ -119,6 +149,18 @@ fi
%exclude /usr/share/doc/*
%changelog
* Wed Apr 03 2024 shixuantong <shixuantong1@huawei.com> - 23.4.1-2
- Type:bugfix
- CVE:NA
- SUG:NA
- DESC:add python3-passlib to BuildRequires
skip some test if there are multiple NICs with the MAC address 'ee:ee:ee:ee:ee:ee'
fix: unpin jsonschema and update tests
test: fix tmpdir in test_cc_apk_configure
bug(tests): mock reads of host's /sys/class/net via get_sys_class_path
test: fix disable_sysfs_net mock
tests: drop CiTestCase and convert to pytest
* Wed Jan 24 2024 shixuantong <shixuantong1@huawei.com> - 23.4.1-1
- Type:enhancement
- CVE:NA