summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Falcon <james.falcon@canonical.com>2021-08-23 16:53:23 -0500
committergit-ubuntu importer <ubuntu-devel-discuss@lists.ubuntu.com>2021-08-23 22:13:08 +0000
commit7d298f273878c2a5308ac3c23b521cd915dd696b (patch)
tree9206d99b1ed39c291bc743a4d9cd5a5449101026
parent4559ea73d29f34098c4ee8c62f9046548bb1ae87 (diff)
downloadcloud-init-git-7d298f273878c2a5308ac3c23b521cd915dd696b.tar.gz
21.3-1-g6803368d-0ubuntu1 (patches unapplied)
Imported using git-ubuntu import.
-rw-r--r--ChangeLog101
-rw-r--r--cloudinit/cmd/devel/logs.py24
-rwxr-xr-xcloudinit/sources/DataSourceAzure.py38
-rw-r--r--cloudinit/ssh_util.py35
-rw-r--r--cloudinit/version.py2
-rw-r--r--debian/changelog18
-rw-r--r--doc/rtd/topics/availability.rst1
-rw-r--r--tests/integration_tests/modules/test_combined.py20
-rw-r--r--tests/integration_tests/modules/test_runcmd.py25
-rw-r--r--tests/integration_tests/modules/test_ssh_keysfile.py138
-rw-r--r--tests/integration_tests/test_upgrade.py11
-rw-r--r--tests/unittests/test_datasource/test_azure.py20
-rw-r--r--tests/unittests/test_datasource/test_vmware.py16
-rw-r--r--tools/.github-cla-signers1
14 files changed, 370 insertions, 80 deletions
diff --git a/ChangeLog b/ChangeLog
index 98528249..6de07ad3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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