diff options
author | James Falcon <james.falcon@canonical.com> | 2021-08-23 16:53:23 -0500 |
---|---|---|
committer | git-ubuntu importer <ubuntu-devel-discuss@lists.ubuntu.com> | 2021-08-23 22:13:08 +0000 |
commit | 7d298f273878c2a5308ac3c23b521cd915dd696b (patch) | |
tree | 9206d99b1ed39c291bc743a4d9cd5a5449101026 | |
parent | 4559ea73d29f34098c4ee8c62f9046548bb1ae87 (diff) | |
download | cloud-init-git-7d298f273878c2a5308ac3c23b521cd915dd696b.tar.gz |
21.3-1-g6803368d-0ubuntu1 (patches unapplied)
Imported using git-ubuntu import.
-rw-r--r-- | ChangeLog | 101 | ||||
-rw-r--r-- | cloudinit/cmd/devel/logs.py | 24 | ||||
-rwxr-xr-x | cloudinit/sources/DataSourceAzure.py | 38 | ||||
-rw-r--r-- | cloudinit/ssh_util.py | 35 | ||||
-rw-r--r-- | cloudinit/version.py | 2 | ||||
-rw-r--r-- | debian/changelog | 18 | ||||
-rw-r--r-- | doc/rtd/topics/availability.rst | 1 | ||||
-rw-r--r-- | tests/integration_tests/modules/test_combined.py | 20 | ||||
-rw-r--r-- | tests/integration_tests/modules/test_runcmd.py | 25 | ||||
-rw-r--r-- | tests/integration_tests/modules/test_ssh_keysfile.py | 138 | ||||
-rw-r--r-- | tests/integration_tests/test_upgrade.py | 11 | ||||
-rw-r--r-- | tests/unittests/test_datasource/test_azure.py | 20 | ||||
-rw-r--r-- | tests/unittests/test_datasource/test_vmware.py | 16 | ||||
-rw-r--r-- | tools/.github-cla-signers | 1 |
14 files changed, 370 insertions, 80 deletions
@@ -1,3 +1,104 @@ +21.3 + - Azure: During primary nic detection, check interface status continuously + before rebinding again (#990) [aswinrajamannar] + - Fix home permissions modified by ssh module (SC-338) (#984) + (LP: #1940233) + - Add integration test for sensitive jinja substitution (#986) + - Ignore hotplug socket when collecting logs (#985) (LP: #1940235) + - testing: Add missing mocks to test_vmware.py (#982) + - add Zadara Edge Cloud Platform to the supported clouds list (#963) + [sarahwzadara] + - testing: skip upgrade tests on LXD VMs (#980) + - Only invoke hotplug socket when functionality is enabled (#952) + - Revert unnecesary lcase in ds-identify (#978) [Andrew Kutz] + - cc_resolv_conf: fix typos (#969) [Shreenidhi Shedi] + - Replace broken httpretty tests with mock (SC-324) (#973) + - Azure: Check if interface is up after sleep when trying to bring it up + (#972) [aswinrajamannar] + - Update dscheck_VMware's rpctool check (#970) [Shreenidhi Shedi] + - Azure: Logging the detected interfaces (#968) [Moustafa Moustafa] + - Change netifaces dependency to 0.10.4 (#965) [Andrew Kutz] + - Azure: Limit polling network metadata on connection errors (#961) + [aswinrajamannar] + - Update inconsistent indentation (#962) [Andrew Kutz] + - cc_puppet: support AIO installations and more (#960) [Gabriel Nagy] + - Add Puppet contributors to CLA signers (#964) [Noah Fontes] + - Datasource for VMware (#953) [Andrew Kutz] + - photon: refactor hostname handling and add networkd activator (#958) + [sshedi] + - Stop copying ssh system keys and check folder permissions (#956) + [Emanuele Giuseppe Esposito] + - testing: port remaining cloud tests to integration testing framework + (SC-191) (#955) + - generate contents for ovf-env.xml when provisioning via IMDS (#959) + [Anh Vo] + - Add support for EuroLinux 7 && EuroLinux 8 (#957) [Aleksander Baranowski] + - Implementing device_aliases as described in docs (#945) + [Mal Graty] (LP: #1867532) + - testing: fix test_ssh_import_id.py (#954) + - Add ability to manage fallback network config on PhotonOS (#941) [sshedi] + - Add VZLinux support (#951) [eb3095] + - VMware: add network-config support in ovf-env.xml (#947) [PengpengSun] + - Update pylint to v2.9.3 and fix the new issues it spots (#946) + [Paride Legovini] + - Azure: mount default provisioning iso before try device listing (#870) + [Anh Vo] + - Document known hotplug limitations (#950) + - Initial hotplug support (#936) + - Fix MIME policy failure on python version upgrade (#934) + - run-container: fixup the centos repos baseurls when using http_proxy + (#944) [Paride Legovini] + - tools: add support for building rpms on rocky linux (#940) + - ssh-util: allow cloudinit to merge all ssh keys into a custom user + file, defined in AuthorizedKeysFile (#937) [Emanuele Giuseppe Esposito] + (LP: #1911680) + - VMware: new "allow_raw_data" switch (#939) [xiaofengw-vmware] + - bump pycloudlib version (#935) + - add renanrodrigo as a contributor (#938) [Renan Rodrigo] + - testing: simplify test_upgrade.py (#932) + - freebsd/net_v1 format: read MTU from root (#930) [Gonéri Le Bouder] + - Add new network activators to bring up interfaces (#919) + - - Detect a Python version change and clear the cache (#857) + [Robert Schweikert] + - cloud_tests: fix the Impish release name (#931) [Paride Legovini] + - Removed distro specific network code from Photon (#929) [sshedi] + - Add support for VMware PhotonOS (#909) [sshedi] + - cloud_tests: add impish release definition (#927) [Paride Legovini] + - docs: fix stale links rename master branch to main (#926) + - Fix DNS in NetworkState (SC-133) (#923) + - tests: Add 'adhoc' mark for integration tests (#925) + - Fix the spelling of "DigitalOcean" (#924) [Mark Mercado] + - Small Doc Update for ReportEventStack and Test (#920) [Mike Russell] + - Replace deprecated collections.Iterable with abc replacement (#922) + (LP: #1932048) + - testing: OCI availability domain is now required (SC-59) (#910) + - add DragonFlyBSD support (#904) [Gonéri Le Bouder] + - Use instance-data-sensitive.json in jinja templates (SC-117) (#917) + (LP: #1931392) + - doc: Update NoCloud docs stating required files (#918) (LP: #1931577) + - build-on-netbsd: don't pin a specific py3 version (#913) + [Gonéri Le Bouder] + - - Create the log file with 640 permissions (#858) [Robert Schweikert] + - Allow braces to appear in dhclient output (#911) [eb3095] + - Docs: Replace all freenode references with libera (#912) + - openbsd/net: flush the route table on net restart (#908) + [Gonéri Le Bouder] + - Add Rocky Linux support to cloud-init (#906) [Louis Abel] + - Add "esposem" as contributor (#907) [Emanuele Giuseppe Esposito] + - Add integration test for #868 (#901) + - Added support for importing keys via primary/security mirror clauses + (#882) [Paul Goins] (LP: #1925395) + - [examples] config-user-groups expire in the future (#902) + [Geert Stappers] + - BSD: static network, set the mtu (#894) [Gonéri Le Bouder] + - Add integration test for lp-1920939 (#891) + - Fix unit tests breaking from new httpretty version (#903) + - Allow user control over update events (#834) + - Update test characters in substitution unit test (#893) + - cc_disk_setup.py: remove UDEVADM_CMD definition as not used (#886) + [dermotbradley] + - Add AlmaLinux OS support (#872) [Andrew Lukoshko] + 21.2 - Add \r\n check for SSH keys in Azure (#889) - Revert "Add support to resize rootfs if using LVM (#721)" (#887) diff --git a/cloudinit/cmd/devel/logs.py b/cloudinit/cmd/devel/logs.py index 51c61cca..31ade73d 100644 --- a/cloudinit/cmd/devel/logs.py +++ b/cloudinit/cmd/devel/logs.py @@ -48,11 +48,15 @@ def get_parser(parser=None): return parser -def _copytree_ignore_sensitive_files(curdir, files): - """Return a list of files to ignore if we are non-root""" - if os.getuid() == 0: - return () - return (INSTANCE_JSON_SENSITIVE_FILE,) # Ignore root-permissioned files +def _copytree_rundir_ignore_files(curdir, files): + """Return a list of files to ignore for /run/cloud-init directory""" + ignored_files = [ + 'hook-hotplug-cmd', # named pipe for hotplug + ] + if os.getuid() != 0: + # Ignore root-permissioned files + ignored_files.append(INSTANCE_JSON_SENSITIVE_FILE) + return ignored_files def _write_command_output_to_file(cmd, filename, msg, verbosity): @@ -123,9 +127,13 @@ def collect_logs(tarfile, include_userdata, verbosity=0): run_dir = os.path.join(log_dir, 'run') ensure_dir(run_dir) if os.path.exists(CLOUDINIT_RUN_DIR): - shutil.copytree(CLOUDINIT_RUN_DIR, - os.path.join(run_dir, 'cloud-init'), - ignore=_copytree_ignore_sensitive_files) + try: + shutil.copytree(CLOUDINIT_RUN_DIR, + os.path.join(run_dir, 'cloud-init'), + ignore=_copytree_rundir_ignore_files) + except shutil.Error as e: + sys.stderr.write("Failed collecting file(s) due to error:\n") + sys.stderr.write(str(e) + '\n') _debug("collected dir %s\n" % CLOUDINIT_RUN_DIR, 1, verbosity) else: _debug("directory '%s' did not exist\n" % CLOUDINIT_RUN_DIR, 1, diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py index ba23139b..fddfe363 100755 --- a/cloudinit/sources/DataSourceAzure.py +++ b/cloudinit/sources/DataSourceAzure.py @@ -892,12 +892,12 @@ class DataSourceAzure(sources.DataSource): logger_func=LOG.info) return - LOG.info("Attempting to bring %s up", ifname) + LOG.debug("Attempting to bring %s up", ifname) attempts = 0 + LOG.info("Unbinding and binding the interface %s", ifname) while True: - LOG.info("Unbinding and binding the interface %s", ifname) devicename = net.read_sys_net(ifname, 'device/device_id').strip('{}') util.write_file('/sys/bus/vmbus/drivers/hv_netvsc/unbind', @@ -912,26 +912,28 @@ class DataSourceAzure(sources.DataSource): report_diagnostic_event(msg, logger_func=LOG.info) return - sleep_duration = 1 - msg = ("Link is not up after %d attempts with %d seconds sleep " - "between attempts." % (attempts, sleep_duration)) - if attempts % 10 == 0: + msg = ("Link is not up after %d attempts to rebind" % attempts) report_diagnostic_event(msg, logger_func=LOG.info) - else: LOG.info(msg) - sleep(sleep_duration) - - # Since we just did a unbind and bind, check again after sleep - # but before doing unbind and bind again to avoid races where the - # link might take a slight delay after bind to be up. - if self.distro.networking.is_up(ifname): - msg = ("Link is up after checking after sleeping for %d secs" - " after %d attempts" % - (sleep_duration, attempts)) - report_diagnostic_event(msg, logger_func=LOG.info) - return + # It could take some time after rebind for the interface to be up. + # So poll for the status for some time before attempting to rebind + # again. + sleep_duration = 0.5 + max_status_polls = 20 + LOG.debug("Polling %d seconds for primary NIC link up after " + "rebind.", sleep_duration * max_status_polls) + + for i in range(0, max_status_polls): + if self.distro.networking.is_up(ifname): + msg = ("After %d attempts to rebind, link is up after " + "polling the link status %d times" % (attempts, i)) + report_diagnostic_event(msg, logger_func=LOG.info) + LOG.debug(msg) + return + else: + sleep(sleep_duration) @azure_ds_telemetry_reporter def _create_report_ready_marker(self): diff --git a/cloudinit/ssh_util.py b/cloudinit/ssh_util.py index b8a3c8f7..9ccadf09 100644 --- a/cloudinit/ssh_util.py +++ b/cloudinit/ssh_util.py @@ -321,23 +321,48 @@ def check_create_path(username, filename, strictmodes): home_folder = os.path.dirname(user_pwent.pw_dir) for directory in directories: parent_folder += "/" + directory - if home_folder.startswith(parent_folder): + + # security check, disallow symlinks in the AuthorizedKeysFile path. + if os.path.islink(parent_folder): + LOG.debug( + "Invalid directory. Symlink exists in path: %s", + parent_folder) + return False + + if os.path.isfile(parent_folder): + LOG.debug( + "Invalid directory. File exists in path: %s", + parent_folder) + return False + + if (home_folder.startswith(parent_folder) or + parent_folder == user_pwent.pw_dir): continue - if not os.path.isdir(parent_folder): + if not os.path.exists(parent_folder): # directory does not exist, and permission so far are good: # create the directory, and make it accessible by everyone # but owned by root, as it might be used by many users. with util.SeLinuxGuard(parent_folder): - os.makedirs(parent_folder, mode=0o755, exist_ok=True) - util.chownbyid(parent_folder, root_pwent.pw_uid, - root_pwent.pw_gid) + mode = 0o755 + uid = root_pwent.pw_uid + gid = root_pwent.pw_gid + if parent_folder.startswith(user_pwent.pw_dir): + mode = 0o700 + uid = user_pwent.pw_uid + gid = user_pwent.pw_gid + os.makedirs(parent_folder, mode=mode, exist_ok=True) + util.chownbyid(parent_folder, uid, gid) permissions = check_permissions(username, parent_folder, filename, False, strictmodes) if not permissions: return False + if os.path.islink(filename) or os.path.isdir(filename): + LOG.debug("%s is not a file!", filename) + return False + # check the file if not os.path.exists(filename): # if file does not exist: we need to create it, since the diff --git a/cloudinit/version.py b/cloudinit/version.py index be47aff3..b798a6d7 100644 --- a/cloudinit/version.py +++ b/cloudinit/version.py @@ -4,7 +4,7 @@ # # This file is part of cloud-init. See LICENSE file for license information. -__VERSION__ = "21.2" +__VERSION__ = "21.3" _PACKAGED_VERSION = '@@PACKAGED_VERSION@@' FEATURES = [ diff --git a/debian/changelog b/debian/changelog index 7055ed68..fc76ca49 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,21 @@ +cloud-init (21.3-1-g6803368d-0ubuntu1) impish; urgency=medium + + * New upstream snapshot. + - testing: Fix ssh keys integration test (#992) + - Release 21.3 (#993) (LP: #1940839) + - Azure: During primary nic detection, check interface status continuously + before rebinding again (#990) [aswinrajamannar] + - Fix home permissions modified by ssh module (SC-338) (#984) + (LP: #1940233) + - Add integration test for sensitive jinja substitution (#986) + - Ignore hotplug socket when collecting logs (#985) (LP: #1940235) + - testing: Add missing mocks to test_vmware.py (#982) + - add Zadara Edge Cloud Platform to the supported clouds list (#963) + [sarahwzadara] + - testing: skip upgrade tests on LXD VMs (#980) + + -- James Falcon <james.falcon@canonical.com> Mon, 23 Aug 2021 16:53:23 -0500 + cloud-init (21.2-69-g65607405-0ubuntu1) impish; urgency=medium * d/cloud-init.templates: Add VMware to default datasource_list diff --git a/doc/rtd/topics/availability.rst b/doc/rtd/topics/availability.rst index 71827177..d8ca9d16 100644 --- a/doc/rtd/topics/availability.rst +++ b/doc/rtd/topics/availability.rst @@ -59,6 +59,7 @@ environments in the public cloud: - SmartOS - UpCloud - Vultr +- Zadara Edge Cloud Platform Additionally, cloud-init is supported on these private clouds: diff --git a/tests/integration_tests/modules/test_combined.py b/tests/integration_tests/modules/test_combined.py index 97b59558..27f3c074 100644 --- a/tests/integration_tests/modules/test_combined.py +++ b/tests/integration_tests/modules/test_combined.py @@ -15,6 +15,7 @@ from tests.integration_tests.instances import IntegrationInstance from tests.integration_tests.util import verify_ordered_items_in_text USER_DATA = """\ +## template: jinja #cloud-config apt: primary: @@ -31,6 +32,9 @@ locale: en_GB.UTF-8 locale_configfile: /etc/default/locale ntp: servers: ['ntp.ubuntu.com'] +runcmd: + - echo {{ds.meta_data.local_hostname}} > /var/tmp/runcmd_output + - echo {{merged_cfg.def_log_file}} >> /var/tmp/runcmd_output """ @@ -92,6 +96,22 @@ class TestCombined: 'en_US.UTF-8' ], locale_gen) + def test_runcmd_with_variable_substitution( + self, class_client: IntegrationInstance + ): + """Test runcmd, while including jinja substitution. + + Ensure we can also substitue variables from instance-data-sensitive + LP: #1931392 + """ + client = class_client + expected = [ + client.execute('hostname').stdout.strip(), + '/var/log/cloud-init.log', + ] + output = client.read_from_file('/var/tmp/runcmd_output') + verify_ordered_items_in_text(expected, output) + def test_no_problems(self, class_client: IntegrationInstance): """Test no errors, warnings, or tracebacks""" client = class_client diff --git a/tests/integration_tests/modules/test_runcmd.py b/tests/integration_tests/modules/test_runcmd.py deleted file mode 100644 index 50d1851e..00000000 --- a/tests/integration_tests/modules/test_runcmd.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Integration test for the runcmd module. - -This test specifies a command to be executed by the ``runcmd`` module -and then checks if that command was executed during boot. - -(This is ported from -``tests/cloud_tests/testcases/modules/runcmd.yaml``.)""" - -import pytest - - -USER_DATA = """\ -#cloud-config -runcmd: - - echo cloud-init run cmd test > /var/tmp/run_cmd -""" - - -@pytest.mark.ci -class TestRuncmd: - - @pytest.mark.user_data(USER_DATA) - def test_runcmd(self, client): - runcmd_output = client.read_from_file("/var/tmp/run_cmd") - assert runcmd_output.strip() == "cloud-init run cmd test" diff --git a/tests/integration_tests/modules/test_ssh_keysfile.py b/tests/integration_tests/modules/test_ssh_keysfile.py index f82d7649..5c720578 100644 --- a/tests/integration_tests/modules/test_ssh_keysfile.py +++ b/tests/integration_tests/modules/test_ssh_keysfile.py @@ -3,6 +3,7 @@ import pytest from io import StringIO from paramiko.ssh_exception import SSHException +from tests.integration_tests.clouds import ImageSpecification from tests.integration_tests.instances import IntegrationInstance from tests.integration_tests.util import get_test_rsa_keypair @@ -10,10 +11,10 @@ TEST_USER1_KEYS = get_test_rsa_keypair('test1') TEST_USER2_KEYS = get_test_rsa_keypair('test2') TEST_DEFAULT_KEYS = get_test_rsa_keypair('test3') -USERDATA = """\ +_USERDATA = """\ #cloud-config bootcmd: - - sed -i 's;#AuthorizedKeysFile.*;AuthorizedKeysFile /etc/ssh/authorized_keys %h/.ssh/authorized_keys2;' /etc/ssh/sshd_config + - {bootcmd} ssh_authorized_keys: - {default} users: @@ -24,27 +25,17 @@ users: - name: test_user2 ssh_authorized_keys: - {user2} -""".format( # noqa: E501 +""".format( + bootcmd='{bootcmd}', default=TEST_DEFAULT_KEYS.public_key, user1=TEST_USER1_KEYS.public_key, user2=TEST_USER2_KEYS.public_key, ) -@pytest.mark.ubuntu -@pytest.mark.user_data(USERDATA) -def test_authorized_keys(client: IntegrationInstance): - expected_keys = [ - ('test_user1', '/home/test_user1/.ssh/authorized_keys2', - TEST_USER1_KEYS), - ('test_user2', '/home/test_user2/.ssh/authorized_keys2', - TEST_USER2_KEYS), - ('ubuntu', '/home/ubuntu/.ssh/authorized_keys2', - TEST_DEFAULT_KEYS), - ('root', '/root/.ssh/authorized_keys2', TEST_DEFAULT_KEYS), - ] - +def common_verify(client, expected_keys): for user, filename, keys in expected_keys: + # Ensure key is in the key file contents = client.read_from_file(filename) if user in ['ubuntu', 'root']: # Our personal public key gets added by pycloudlib @@ -83,3 +74,118 @@ def test_authorized_keys(client: IntegrationInstance): look_for_keys=False, allow_agent=False, ) + + # Ensure we haven't messed with any /home permissions + # See LP: #1940233 + home_dir = '/home/{}'.format(user) + # Home permissions aren't consistent between releases. On ubuntu + # this can change to 750 once focal is unsupported. + if ImageSpecification.from_os_image().release in ("bionic", "focal"): + home_perms = '755' + else: + home_perms = '750' + if user == 'root': + home_dir = '/root' + home_perms = '700' + assert '{} {}'.format(user, home_perms) == client.execute( + 'stat -c "%U %a" {}'.format(home_dir) + ) + if client.execute("test -d {}/.ssh".format(home_dir)).ok: + assert '{} 700'.format(user) == client.execute( + 'stat -c "%U %a" {}/.ssh'.format(home_dir) + ) + assert '{} 600'.format(user) == client.execute( + 'stat -c "%U %a" {}'.format(filename) + ) + + # Also ensure ssh-keygen works as expected + client.execute('mkdir {}/.ssh'.format(home_dir)) + assert client.execute( + "ssh-keygen -b 2048 -t rsa -f {}/.ssh/id_rsa -q -N ''".format( + home_dir) + ).ok + assert client.execute('test -f {}/.ssh/id_rsa'.format(home_dir)) + assert client.execute('test -f {}/.ssh/id_rsa.pub'.format(home_dir)) + + assert 'root 755' == client.execute('stat -c "%U %a" /home') + + +DEFAULT_KEYS_USERDATA = _USERDATA.format(bootcmd='""') + + +@pytest.mark.ubuntu +@pytest.mark.user_data(DEFAULT_KEYS_USERDATA) +def test_authorized_keys_default(client: IntegrationInstance): + expected_keys = [ + ('test_user1', '/home/test_user1/.ssh/authorized_keys', + TEST_USER1_KEYS), + ('test_user2', '/home/test_user2/.ssh/authorized_keys', + TEST_USER2_KEYS), + ('ubuntu', '/home/ubuntu/.ssh/authorized_keys', + TEST_DEFAULT_KEYS), + ('root', '/root/.ssh/authorized_keys', TEST_DEFAULT_KEYS), + ] + common_verify(client, expected_keys) + + +AUTHORIZED_KEYS2_USERDATA = _USERDATA.format(bootcmd=( + "sed -i 's;#AuthorizedKeysFile.*;AuthorizedKeysFile " + "/etc/ssh/authorized_keys %h/.ssh/authorized_keys2;' " + "/etc/ssh/sshd_config")) + + +@pytest.mark.ubuntu +@pytest.mark.user_data(AUTHORIZED_KEYS2_USERDATA) +def test_authorized_keys2(client: IntegrationInstance): + expected_keys = [ + ('test_user1', '/home/test_user1/.ssh/authorized_keys2', + TEST_USER1_KEYS), + ('test_user2', '/home/test_user2/.ssh/authorized_keys2', + TEST_USER2_KEYS), + ('ubuntu', '/home/ubuntu/.ssh/authorized_keys2', + TEST_DEFAULT_KEYS), + ('root', '/root/.ssh/authorized_keys2', TEST_DEFAULT_KEYS), + ] + common_verify(client, expected_keys) + + +NESTED_KEYS_USERDATA = _USERDATA.format(bootcmd=( + "sed -i 's;#AuthorizedKeysFile.*;AuthorizedKeysFile " + "/etc/ssh/authorized_keys %h/foo/bar/ssh/keys;' " + "/etc/ssh/sshd_config")) + + +@pytest.mark.ubuntu +@pytest.mark.user_data(NESTED_KEYS_USERDATA) +def test_nested_keys(client: IntegrationInstance): + expected_keys = [ + ('test_user1', '/home/test_user1/foo/bar/ssh/keys', + TEST_USER1_KEYS), + ('test_user2', '/home/test_user2/foo/bar/ssh/keys', + TEST_USER2_KEYS), + ('ubuntu', '/home/ubuntu/foo/bar/ssh/keys', + TEST_DEFAULT_KEYS), + ('root', '/root/foo/bar/ssh/keys', TEST_DEFAULT_KEYS), + ] + common_verify(client, expected_keys) + + +EXTERNAL_KEYS_USERDATA = _USERDATA.format(bootcmd=( + "sed -i 's;#AuthorizedKeysFile.*;AuthorizedKeysFile " + "/etc/ssh/authorized_keys /etc/ssh/authorized_keys/%u/keys;' " + "/etc/ssh/sshd_config")) + + +@pytest.mark.ubuntu +@pytest.mark.user_data(EXTERNAL_KEYS_USERDATA) +def test_external_keys(client: IntegrationInstance): + expected_keys = [ + ('test_user1', '/etc/ssh/authorized_keys/test_user1/keys', + TEST_USER1_KEYS), + ('test_user2', '/etc/ssh/authorized_keys/test_user2/keys', + TEST_USER2_KEYS), + ('ubuntu', '/etc/ssh/authorized_keys/ubuntu/keys', + TEST_DEFAULT_KEYS), + ('root', '/etc/ssh/authorized_keys/root/keys', TEST_DEFAULT_KEYS), + ] + common_verify(client, expected_keys) diff --git a/tests/integration_tests/test_upgrade.py b/tests/integration_tests/test_upgrade.py index 5c7a2e1e..376fcc96 100644 --- a/tests/integration_tests/test_upgrade.py +++ b/tests/integration_tests/test_upgrade.py @@ -3,7 +3,7 @@ import logging import os import pytest -from tests.integration_tests.clouds import IntegrationCloud +from tests.integration_tests.clouds import ImageSpecification, IntegrationCloud from tests.integration_tests.conftest import get_validated_source @@ -45,6 +45,15 @@ def test_clean_boot_of_upgraded_package(session_cloud: IntegrationCloud): if not source.installs_new_version(): pytest.skip(UNSUPPORTED_INSTALL_METHOD_MSG.format(source)) return # type checking doesn't understand that skip raises + if (ImageSpecification.from_os_image().release == 'bionic' and + session_cloud.settings.PLATFORM == 'lxd_vm'): + # The issues that we see on Bionic VMs don't appear anywhere + # else, including when calling KVM directly. It likely has to + # do with the extra lxd-agent setup happening on bionic. + # Given that we still have Bionic covered on all other platforms, + # the risk of skipping bionic here seems low enough. + pytest.skip("Upgrade test doesn't run on LXD VMs and bionic") + return launch_kwargs = { 'image_id': session_cloud.released_image_id, diff --git a/tests/unittests/test_datasource/test_azure.py b/tests/unittests/test_datasource/test_azure.py index 03609c3d..851cf82e 100644 --- a/tests/unittests/test_datasource/test_azure.py +++ b/tests/unittests/test_datasource/test_azure.py @@ -2912,19 +2912,29 @@ class TestPreprovisioningHotAttachNics(CiTestCase): @mock.patch('cloudinit.net.read_sys_net') @mock.patch('cloudinit.distros.networking.LinuxNetworking.try_set_link_up') def test_wait_for_link_up_checks_link_after_sleep( - self, m_is_link_up, m_read_sys_net, m_writefile, m_is_up): + self, m_try_set_link_up, m_read_sys_net, m_writefile, m_is_up): """Waiting for link to be up should return immediately if the link is already up.""" distro_cls = distros.fetch('ubuntu') distro = distro_cls('ubuntu', {}, self.paths) dsa = dsaz.DataSourceAzure({}, distro=distro, paths=self.paths) - m_is_link_up.return_value = False - m_is_up.return_value = True + m_try_set_link_up.return_value = False + + callcount = 0 + + def is_up_mock(key): + nonlocal callcount + if callcount == 0: + callcount += 1 + return False + return True + + m_is_up.side_effect = is_up_mock dsa.wait_for_link_up("eth0") - self.assertEqual(2, m_is_link_up.call_count) - self.assertEqual(1, m_is_up.call_count) + self.assertEqual(2, m_try_set_link_up.call_count) + self.assertEqual(2, m_is_up.call_count) @mock.patch(MOCKPATH + 'util.write_file') @mock.patch('cloudinit.net.read_sys_net') diff --git a/tests/unittests/test_datasource/test_vmware.py b/tests/unittests/test_datasource/test_vmware.py index 597db7c8..52f910b5 100644 --- a/tests/unittests/test_datasource/test_vmware.py +++ b/tests/unittests/test_datasource/test_vmware.py @@ -6,6 +6,10 @@ import base64 import gzip +import os + +import pytest + from cloudinit import dmi, helpers, safeyaml from cloudinit import settings from cloudinit.sources import DataSourceVMware @@ -16,7 +20,6 @@ from cloudinit.tests.helpers import ( populate_dir, ) -import os PRODUCT_NAME_FILE_PATH = "/sys/class/dmi/id/product_name" PRODUCT_NAME = "VMware7,1" @@ -56,6 +59,17 @@ runcmd: """ +@pytest.yield_fixture(autouse=True) +def common_patches(): + with mock.patch('cloudinit.util.platform.platform', return_value='Linux'): + with mock.patch.multiple( + 'cloudinit.dmi', + is_container=mock.Mock(return_value=False), + is_FreeBSD=mock.Mock(return_value=False) + ): + yield + + class TestDataSourceVMware(CiTestCase): """ Test common functionality that is not transport specific. diff --git a/tools/.github-cla-signers b/tools/.github-cla-signers index cf06ca3d..4fa108aa 100644 --- a/tools/.github-cla-signers +++ b/tools/.github-cla-signers @@ -51,6 +51,7 @@ onitake qubidt renanrodrigo riedel +sarahwzadara slyon smoser sshedi |