summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/integration_tests/modules/test_package_update_upgrade_install.py74
-rw-r--r--tests/integration_tests/modules/test_runcmd.py24
-rw-r--r--tests/integration_tests/modules/test_seed_random_data.py27
-rw-r--r--tests/integration_tests/modules/test_set_hostname.py46
-rw-r--r--tests/integration_tests/modules/test_snap.py28
-rw-r--r--tests/integration_tests/modules/test_ssh_auth_key_fingerprints.py47
-rw-r--r--tests/integration_tests/modules/test_ssh_generate.py50
-rw-r--r--tests/integration_tests/modules/test_ssh_import_id.py28
-rw-r--r--tests/integration_tests/modules/test_ssh_keys_provided.py134
-rw-r--r--tests/integration_tests/modules/test_timezone.py24
-rw-r--r--tests/integration_tests/modules/test_write_files.py65
11 files changed, 547 insertions, 0 deletions
diff --git a/tests/integration_tests/modules/test_package_update_upgrade_install.py b/tests/integration_tests/modules/test_package_update_upgrade_install.py
new file mode 100644
index 00000000..8a38ad84
--- /dev/null
+++ b/tests/integration_tests/modules/test_package_update_upgrade_install.py
@@ -0,0 +1,74 @@
+"""Integration test for the package update upgrade install module.
+
+This test module asserts that packages are upgraded/updated during boot
+with the ``package_update_upgrade_install`` module. We are also testing
+if we can install new packages during boot too.
+
+(This is ported from
+``tests/cloud_tests/testcases/modules/package_update_upgrade_install.yaml``.)
+
+NOTE: the testcase for this looks for the command in history.log as
+ /usr/bin/apt-get..., which is not how it always appears. it should
+ instead look for just apt-get...
+"""
+
+import re
+import pytest
+
+
+USER_DATA = """\
+#cloud-config
+packages:
+ - sl
+ - tree
+package_update: true
+package_upgrade: true
+"""
+
+
+@pytest.mark.user_data(USER_DATA)
+class TestPackageUpdateUpgradeInstall:
+
+ def assert_package_installed(self, pkg_out, name, version=None):
+ """Check dpkg-query --show output for matching package name.
+
+ @param name: package base name
+ @param version: string representing a package version or part of a
+ version.
+ """
+ pkg_match = re.search(
+ "^%s\t(?P<version>.*)$" % name, pkg_out, re.MULTILINE)
+ if pkg_match:
+ installed_version = pkg_match.group("version")
+ if not version:
+ return # Success
+ if installed_version.startswith(version):
+ return # Success
+ raise AssertionError(
+ "Expected package version %s-%s not found. Found %s" %
+ name, version, installed_version)
+ raise AssertionError("Package not installed: %s" % name)
+
+ def test_new_packages_are_installed(self, class_client):
+ pkg_out = class_client.execute("dpkg-query --show")
+
+ self.assert_package_installed(pkg_out, "sl")
+ self.assert_package_installed(pkg_out, "tree")
+
+ def test_packages_were_updated(self, class_client):
+ out = class_client.execute(
+ "grep ^Commandline: /var/log/apt/history.log")
+ assert (
+ "Commandline: /usr/bin/apt-get --option=Dpkg::Options"
+ "::=--force-confold --option=Dpkg::options::=--force-unsafe-io "
+ "--assume-yes --quiet install sl tree") in out
+
+ def test_packages_were_upgraded(self, class_client):
+ """Test cloud-init-output for install & upgrade stuff."""
+ out = class_client.read_from_file("/var/log/cloud-init-output.log")
+ assert "Setting up tree (" in out
+ assert "Setting up sl (" in out
+ assert "Reading package lists..." in out
+ assert "Building dependency tree..." in out
+ assert "Reading state information..." in out
+ assert "Calculating upgrade..." in out
diff --git a/tests/integration_tests/modules/test_runcmd.py b/tests/integration_tests/modules/test_runcmd.py
new file mode 100644
index 00000000..eabe778d
--- /dev/null
+++ b/tests/integration_tests/modules/test_runcmd.py
@@ -0,0 +1,24 @@
+"""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
+"""
+
+
+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_seed_random_data.py b/tests/integration_tests/modules/test_seed_random_data.py
new file mode 100644
index 00000000..db3d2193
--- /dev/null
+++ b/tests/integration_tests/modules/test_seed_random_data.py
@@ -0,0 +1,27 @@
+"""Integration test for the random seed module.
+
+This test specifies a command to be executed by the ``seed_random`` module, by
+providing a different data to be used as seed data. We will then check
+if that seed data was actually used.
+
+(This is ported from
+``tests/cloud_tests/testcases/modules/seed_random_data.yaml``.)"""
+
+import pytest
+
+
+USER_DATA = """\
+#cloud-config
+random_seed:
+ data: 'MYUb34023nD:LFDK10913jk;dfnk:Df'
+ encoding: raw
+ file: /root/seed
+"""
+
+
+class TestSeedRandomData:
+
+ @pytest.mark.user_data(USER_DATA)
+ def test_seed_random_data(self, client):
+ seed_output = client.read_from_file("/root/seed")
+ assert seed_output.strip() == "MYUb34023nD:LFDK10913jk;dfnk:Df"
diff --git a/tests/integration_tests/modules/test_set_hostname.py b/tests/integration_tests/modules/test_set_hostname.py
new file mode 100644
index 00000000..ff46feb9
--- /dev/null
+++ b/tests/integration_tests/modules/test_set_hostname.py
@@ -0,0 +1,46 @@
+"""Integration test for the set_hostname module.
+
+This module specify two tests: One updates only the hostname and the other
+one updates the hostname and fqdn of the system. For both of these tests
+we will check is the changes requested by the user data are being respected
+after the system is boot.
+
+(This is ported from
+``tests/cloud_tests/testcases/modules/set_hostname.yaml`` and
+``tests/cloud_tests/testcases/modules/set_hostname_fqdn.yaml``.)"""
+
+import pytest
+
+
+USER_DATA_HOSTNAME = """\
+#cloud-config
+hostname: cloudinit2
+"""
+
+USER_DATA_FQDN = """\
+#cloud-config
+manage_etc_hosts: true
+hostname: cloudinit1
+fqdn: cloudinit2.i9n.cloud-init.io
+"""
+
+
+class TestHostname:
+
+ @pytest.mark.user_data(USER_DATA_HOSTNAME)
+ def test_hostname(self, client):
+ hostname_output = client.execute("hostname")
+ assert "cloudinit2" in hostname_output.strip()
+
+ @pytest.mark.user_data(USER_DATA_FQDN)
+ def test_hostname_and_fqdn(self, client):
+ hostname_output = client.execute("hostname")
+ assert "cloudinit1" in hostname_output.strip()
+
+ fqdn_output = client.execute("hostname --fqdn")
+ assert "cloudinit2.i9n.cloud-init.io" in fqdn_output.strip()
+
+ host_output = client.execute("grep ^127 /etc/hosts")
+ assert '127.0.1.1 {} {}'.format(
+ fqdn_output, hostname_output) in host_output
+ assert '127.0.0.1 localhost' in host_output
diff --git a/tests/integration_tests/modules/test_snap.py b/tests/integration_tests/modules/test_snap.py
new file mode 100644
index 00000000..d78a0b1e
--- /dev/null
+++ b/tests/integration_tests/modules/test_snap.py
@@ -0,0 +1,28 @@
+"""Integration test for the snap module.
+
+This test specifies a command to be executed by the ``snap`` module
+and then checks that if that command was executed during boot.
+
+(This is ported from
+``tests/cloud_tests/testcases/modules/runcmd.yaml``.)"""
+
+import pytest
+
+
+USER_DATA = """\
+#cloud-config
+package_update: true
+snap:
+ squashfuse_in_container: true
+ commands:
+ - snap install hello-world
+"""
+
+
+class TestSnap:
+
+ @pytest.mark.user_data(USER_DATA)
+ def test_snap(self, client):
+ snap_output = client.execute("snap list")
+ assert "core " in snap_output
+ assert "hello-world " in snap_output
diff --git a/tests/integration_tests/modules/test_ssh_auth_key_fingerprints.py b/tests/integration_tests/modules/test_ssh_auth_key_fingerprints.py
new file mode 100644
index 00000000..e88d9a02
--- /dev/null
+++ b/tests/integration_tests/modules/test_ssh_auth_key_fingerprints.py
@@ -0,0 +1,47 @@
+"""Integration test for the ssh_authkey_fingerprints module.
+
+This modules specifies two tests regarding the ``ssh_authkey_fingerprints``
+module. The first one verifies that we can disable the module behavior while
+the second one verifies if the module is working as expected if enabled.
+
+(This is ported from
+``tests/cloud_tests/testcases/modules/ssh_auth_key_fingerprints_disable.yaml``,
+``tests/cloud_tests/testcases/modules/ssh_auth_key_fingerprints_enable.yaml``.
+)"""
+import re
+
+import pytest
+
+
+USER_DATA_SSH_AUTHKEY_DISABLE = """\
+#cloud-config
+no_ssh_fingerprints: true
+"""
+
+USER_DATA_SSH_AUTHKEY_ENABLE="""\
+#cloud-config
+ssh_genkeytypes:
+ - ecdsa
+ - ed25519
+ssh_authorized_keys:
+ - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDXW9Gg5H7ehjdSc6qDzwNtgCy94XYHhEYlXZMO2+FJrH3wfHGiMfCwOHxcOMt2QiXItULthdeQWS9QjBSSjVRXf6731igFrqPFyS9qBlOQ5D29C4HBXFnQggGVpBNJ82IRJv7szbbe/vpgLBP4kttUza9Dr4e1YM1ln4PRnjfXea6T0m+m1ixNb5432pTXlqYOnNOxSIm1gHgMLxPuDrJvQERDKrSiKSjIdyC9Jd8t2e1tkNLY0stmckVRbhShmcJvlyofHWbc2Ca1mmtP7MlS1VQnfLkvU1IrFwkmaQmaggX6WR6coRJ6XFXdWcq/AI2K6GjSnl1dnnCxE8VCEXBlXgFzad+PMSG4yiL5j8Oo1ZVpkTdgBnw4okGqTYCXyZg6X00As9IBNQfZMFlQXlIo4FiWgj3CO5QHQOyOX6FuEumaU13GnERrSSdp9tCs1Qm3/DG2RSCQBWTfcgMcStIvKqvJ3IjFn0vGLvI3Ampnq9q1SHwmmzAPSdzcMA76HyMUA5VWaBvWHlUxzIM6unxZASnwvuCzpywSEB5J2OF+p6H+cStJwQ32XwmOG8pLp1srlVWpqZI58Du/lzrkPqONphoZx0LDV86w7RUz1ksDzAdcm0tvmNRFMN1a0frDs506oA3aWK0oDk4Nmvk8sXGTYYw3iQSkOvDUUlIsqdaO+w==
+""" # noqa
+
+
+class TestSshAuthkeyFingerprints:
+
+ @pytest.mark.user_data(USER_DATA_SSH_AUTHKEY_DISABLE)
+ def test_ssh_authkey_fingerprints_disable(self, client):
+ cloudinit_output = client.read_from_file("/var/log/cloud-init.log")
+ assert (
+ "Skipping module named ssh-authkey-fingerprints, "
+ "logging of SSH fingerprints disabled") in cloudinit_output
+
+ @pytest.mark.user_data(USER_DATA_SSH_AUTHKEY_ENABLE)
+ def test_ssh_authkey_fingerprints_enable(self, client):
+ syslog_output = client.read_from_file("/var/log/syslog")
+
+ assert re.search(r'256 SHA256:.*(ECDSA)', syslog_output) is not None
+ assert re.search(r'256 SHA256:.*(ED25519)', syslog_output) is not None
+ assert re.search(r'1024 SHA256:.*(DSA)', syslog_output) is None
+ assert re.search(r'2048 SHA256:.*(RSA)', syslog_output) is None
diff --git a/tests/integration_tests/modules/test_ssh_generate.py b/tests/integration_tests/modules/test_ssh_generate.py
new file mode 100644
index 00000000..8c60fb87
--- /dev/null
+++ b/tests/integration_tests/modules/test_ssh_generate.py
@@ -0,0 +1,50 @@
+"""Integration test for the ssh module.
+
+This module has two tests to verify if we can create ssh keys
+through the ``ssh`` module. The first test asserts that some keys
+were not created while the second one verifies if the expected
+keys were created.
+
+(This is ported from
+``tests/cloud_tests/testcases/modules/ssh_keys_generate.yaml``.)"""
+
+import pytest
+
+
+USER_DATA = """\
+#cloud-config
+ssh_genkeytypes:
+ - ecdsa
+ - ed25519
+authkey_hash: sha512
+"""
+
+
+@pytest.mark.user_data(USER_DATA)
+class TestSshKeysGenerate:
+
+ @pytest.mark.parametrize(
+ "ssh_key_path", (
+ "/etc/ssh/ssh_host_dsa_key.pub",
+ "/etc/ssh/ssh_host_dsa_key",
+ "/etc/ssh/ssh_host_rsa_key.pub",
+ "/etc/ssh/ssh_host_rsa_key",
+ )
+ )
+ def test_ssh_keys_not_generated(self, ssh_key_path, class_client):
+ out = class_client.execute(
+ "test -e {}".format(ssh_key_path)
+ )
+ assert out.failed
+
+ @pytest.mark.parametrize(
+ "ssh_key_path", (
+ "/etc/ssh/ssh_host_ecdsa_key.pub",
+ "/etc/ssh/ssh_host_ecdsa_key",
+ "/etc/ssh/ssh_host_ed25519_key.pub",
+ "/etc/ssh/ssh_host_ed25519_key",
+ )
+ )
+ def test_ssh_keys_generated(self, ssh_key_path, class_client):
+ out = class_client.read_from_file(ssh_key_path)
+ assert "" != out.strip()
diff --git a/tests/integration_tests/modules/test_ssh_import_id.py b/tests/integration_tests/modules/test_ssh_import_id.py
new file mode 100644
index 00000000..2f2ac92c
--- /dev/null
+++ b/tests/integration_tests/modules/test_ssh_import_id.py
@@ -0,0 +1,28 @@
+"""Integration test for the ssh_import_id module.
+
+This test specifies ssh keys to be imported by the ``ssh_import_id`` module
+and then checks that if the ssh keys were successfully imported.
+
+(This is ported from
+``tests/cloud_tests/testcases/modules/ssh_import_id.yaml``.)"""
+
+import pytest
+
+
+USER_DATA = """\
+#cloud-config
+ssh_import_id:
+ - gh:powersj
+ - lp:smoser
+"""
+
+
+class TestSshImportId:
+
+ @pytest.mark.user_data(USER_DATA)
+ def test_ssh_import_id(self, client):
+ ssh_output = client.read_from_file(
+ "/home/ubuntu/.ssh/authorized_keys")
+
+ assert '# ssh-import-id gh:powersj' in ssh_output
+ assert '# ssh-import-id lp:smoser' in ssh_output
diff --git a/tests/integration_tests/modules/test_ssh_keys_provided.py b/tests/integration_tests/modules/test_ssh_keys_provided.py
new file mode 100644
index 00000000..4699518d
--- /dev/null
+++ b/tests/integration_tests/modules/test_ssh_keys_provided.py
@@ -0,0 +1,134 @@
+"""Integration test for the ssh module.
+
+This test specifies keys to be provided to the system through the ``ssh``
+module and then checks that if those keys were successfully added to the
+system.
+
+(This is ported from
+``tests/cloud_tests/testcases/modules/ssh_keys_provided.yaml''``.)"""
+
+import pytest
+
+
+USER_DATA = """\
+#cloud-config
+disable_root: false
+ssh_authorized_keys:
+ - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDXW9Gg5H7ehjdSc6qDzwNtgCy94XYHhEYlXZMO2+FJrH3wfHGiMfCwOHxcOMt2QiXItULthdeQWS9QjBSSjVRXf6731igFrqPFyS9qBlOQ5D29C4HBXFnQggGVpBNJ82IRJv7szbbe/vpgLBP4kttUza9Dr4e1YM1ln4PRnjfXea6T0m+m1ixNb5432pTXlqYOnNOxSIm1gHgMLxPuDrJvQERDKrSiKSjIdyC9Jd8t2e1tkNLY0stmckVRbhShmcJvlyofHWbc2Ca1mmtP7MlS1VQnfLkvU1IrFwkmaQmaggX6WR6coRJ6XFXdWcq/AI2K6GjSnl1dnnCxE8VCEXBlXgFzad+PMSG4yiL5j8Oo1ZVpkTdgBnw4okGqTYCXyZg6X00As9IBNQfZMFlQXlIo4FiWgj3CO5QHQOyOX6FuEumaU13GnERrSSdp9tCs1Qm3/DG2RSCQBWTfcgMcStIvKqvJ3IjFn0vGLvI3Ampnq9q1SHwmmzAPSdzcMA76HyMUA5VWaBvWHlUxzIM6unxZASnwvuCzpywSEB5J2OF+p6H+cStJwQ32XwmOG8pLp1srlVWpqZI58Du/lzrkPqONphoZx0LDV86w7RUz1ksDzAdcm0tvmNRFMN1a0frDs506oA3aWK0oDk4Nmvk8sXGTYYw3iQSkOvDUUlIsqdaO+w==
+ssh_keys:
+ rsa_private: |
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIEowIBAAKCAQEAtPx6PqN3iSEsnTtibyIEy52Tra8T5fn0ryXyg46Di2NBwdnj
+ o8trNv9jenfV/UhmePl58lXjT43wV8OCMl6KsYXyBdegM35NNtono4I4mLLKFMR9
+ 9TOtDn6iYcaNenVhF3ZCj9Z2nNOlTrdc0uchHqKMrxLjCRCUrL91Uf+xioTF901Y
+ RM+ZqC5lT92yAL76F4qPF+Lq1QtUfNfUIwwvOp5ccDZLPxij0YvyBzubYye9hJHu
+ yjbJv78R4JHV+L2WhzSoX3W/6WrxVzeXqFGqH894ccOaC/7tnqSP6V8lIQ6fE2+c
+ DurJcpM3CJRgkndGHjtU55Y71YkcdLksSMvezQIDAQABAoIBAQCrU4IJP8dNeaj5
+ IpkY6NQvR/jfZqfogYi+MKb1IHin/4rlDfUvPcY9pt8ttLlObjYK+OcWn3Vx/sRw
+ 4DOkqNiUGl80Zp1RgZNohHUXlJMtAbrIlAVEk+mTmg7vjfyp2unRQvLZpMRdywBm
+ lq95OrCghnG03aUsFJUZPpi5ydnwbA12ma+KHkG0EzaVlhA7X9N6z0K6U+zue2gl
+ goMLt/MH0rsYawkHrwiwXaIFQeyV4MJP0vmrZLbFk1bycu9X/xPtTYotWyWo4eKA
+ cb05uu04qwexkKHDM0KXtT0JecbTo2rOefFo8Uuab6uJY+fEHNocZ+v1vLA4aOxJ
+ ovp1JuXlAoGBAOWYNgKrlTfy5n0sKsNk+1RuL2jHJZJ3HMd0EIt7/fFQN3Fi08Hu
+ jtntqD30Wj+DJK8b8Lrt66FruxyEJm5VhVmwkukrLR5ige2f6ftZnoFCmdyy+0zP
+ dnPZSUe2H5ZPHa+qthJgHLn+al2P04tGh+1fGHC2PbP+e0Co+/ZRIOxrAoGBAMnN
+ IEen9/FRsqvnDd36I8XnJGskVRTZNjylxBmbKcuMWm+gNhOI7gsCAcqzD4BYZjjW
+ pLhrt/u9p+l4MOJy6OUUdM/okg12SnJEGryysOcVBcXyrvOfklWnANG4EAH5jt1N
+ ftTb1XTxzvWVuR/WJK0B5MZNYM71cumBdUDtPi+nAoGAYmoIXMSnxb+8xNL10aOr
+ h9ljQQp8NHgSQfyiSufvRk0YNuYh1vMnEIsqnsPrG2Zfhx/25GmvoxXGssaCorDN
+ 5FAn6QK06F1ZTD5L0Y3sv4OI6G1gAuC66ZWuL6sFhyyKkQ4f1WiVZ7SCa3CHQSAO
+ i9VDaKz1bf4bXvAQcNj9v9kCgYACSOZCqW4vN0OUmqsXhkt9ZB6Pb/veno70pNPR
+ jmYsvcwQU3oJQpWfXkhy6RAV3epaXmPDCsUsfns2M3wqNC7a2R5xdCqjKGGzZX4A
+ AO3rz9se4J6Gd5oKijeCKFlWDGNHsibrdgm2pz42nZlY+O21X74dWKbt8O16I1MW
+ hxkbJQKBgAXfuen/srVkJgPuqywUYag90VWCpHsuxdn+fZJa50SyZADr+RbiDfH2
+ vek8Uo8ap8AEsv4Rfs9opUcUZevLp3g2741eOaidHVLm0l4iLIVl03otGOqvSzs+
+ A3tFPEOxauXpzCt8f8eXsz0WQXAgIKW2h8zu5QHjomioU3i27mtE
+ -----END RSA PRIVATE KEY-----
+ rsa_public: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0/Ho+o3eJISydO2JvIgTLnZOtrxPl+fSvJfKDjoOLY0HB2eOjy2s2/2N6d9X9SGZ4+XnyVeNPjfBXw4IyXoqxhfIF16Azfk022iejgjiYssoUxH31M60OfqJhxo16dWEXdkKP1nac06VOt1zS5yEeooyvEuMJEJSsv3VR/7GKhMX3TVhEz5moLmVP3bIAvvoXio8X4urVC1R819QjDC86nlxwNks/GKPRi/IHO5tjJ72Eke7KNsm/vxHgkdX4vZaHNKhfdb/pavFXN5eoUaofz3hxw5oL/u2epI/pXyUhDp8Tb5wO6slykzcIlGCSd0YeO1TnljvViRx0uSxIy97N root@xenial-lxd
+ dsa_private: |
+ -----BEGIN DSA PRIVATE KEY-----
+ MIIBuwIBAAKBgQD5Fstc23IVSDe6k4DNP8smPKuEWUvHDTOGaXrhOVAfzZ6+jklP
+ 55mzvC7jO53PWWC31hq10xBoWdev0WtcNF9Tv+4bAa1263y51Rqo4GI7xx+xic1d
+ mLqqfYijBT9k48J/1tV0cs1Wjs6FP/IJTD/kYVC930JjYQMi722lBnUxsQIVAL7i
+ z3fTGKTvSzvW0wQlwnYpS2QFAoGANp+KdyS9V93HgxGQEN1rlj/TSv/a3EVdCKtE
+ nQf55aPHxDAVDVw5JtRh4pZbbRV4oGRPc9KOdjo5BU28vSM3Lmhkb+UaaDXwHkgI
+ nK193o74DKjADWZxuLyyiKHiMOhxozoxDfjWxs8nz6uqvSW0pr521EwIY6RajbED
+ nZ2a3GkCgYEAyoUomNRB6bmpsIfzt8zdtqLP5umIj2uhr9MVPL8/QdbxmJ72Z7pf
+ Q2z1B7QAdIBGOlqJXtlau7ABhWK29Efe+99ObyTSSdDc6RCDeAwUmBAiPRQhDH2E
+ wExw3doDSCUb28L1B50wBzQ8mC3KXp6C7IkBXWspb16DLHUHFSI8bkICFA5kVUcW
+ nCPOXEQsayANi8+Cb7BH
+ -----END DSA PRIVATE KEY-----
+ dsa_public: ssh-dss AAAAB3NzaC1kc3MAAACBAPkWy1zbchVIN7qTgM0/yyY8q4RZS8cNM4ZpeuE5UB/Nnr6OSU/nmbO8LuM7nc9ZYLfWGrXTEGhZ16/Ra1w0X1O/7hsBrXbrfLnVGqjgYjvHH7GJzV2Yuqp9iKMFP2Tjwn/W1XRyzVaOzoU/8glMP+RhUL3fQmNhAyLvbaUGdTGxAAAAFQC+4s930xik70s71tMEJcJ2KUtkBQAAAIA2n4p3JL1X3ceDEZAQ3WuWP9NK/9rcRV0Iq0SdB/nlo8fEMBUNXDkm1GHillttFXigZE9z0o52OjkFTby9IzcuaGRv5RpoNfAeSAicrX3ejvgMqMANZnG4vLKIoeIw6HGjOjEN+NbGzyfPq6q9JbSmvnbUTAhjpFqNsQOdnZrcaQAAAIEAyoUomNRB6bmpsIfzt8zdtqLP5umIj2uhr9MVPL8/QdbxmJ72Z7pfQ2z1B7QAdIBGOlqJXtlau7ABhWK29Efe+99ObyTSSdDc6RCDeAwUmBAiPRQhDH2EwExw3doDSCUb28L1B50wBzQ8mC3KXp6C7IkBXWspb16DLHUHFSI8bkI= root@xenial-lxd
+ ed25519_private: |
+ -----BEGIN OPENSSH PRIVATE KEY-----
+ b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+ QyNTUxOQAAACDbnQGUruL42aVVsyHeaV5mYNTOhteXao0Nl5DVThJ2+QAAAJgwt+lcMLfp
+ XAAAAAtzc2gtZWQyNTUxOQAAACDbnQGUruL42aVVsyHeaV5mYNTOhteXao0Nl5DVThJ2+Q
+ AAAEDQlFZpz9q8+/YJHS9+jPAqy2ZT6cGEv8HTB6RZtTjd/dudAZSu4vjZpVWzId5pXmZg
+ 1M6G15dqjQ2XkNVOEnb5AAAAD3Jvb3RAeGVuaWFsLWx4ZAECAwQFBg==
+ -----END OPENSSH PRIVATE KEY-----
+ ed25519_public: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINudAZSu4vjZpVWzId5pXmZg1M6G15dqjQ2XkNVOEnb5 root@xenial-lxd
+ ecdsa_private: |
+ -----BEGIN EC PRIVATE KEY-----
+ MHcCAQEEIDuK+QFc1wmyJY8uDqQVa1qHte30Rk/fdLxGIBkwJAyOoAoGCCqGSM49
+ AwEHoUQDQgAEWxLlO+TL8gL91eET9p/HFQbqR1A691AkJgZk3jY5mpZqxgX4vcgb
+ 7f/CtXuM6s2svcDJqAeXr6Wk8OJJcMxylA==
+ -----END EC PRIVATE KEY-----
+ ecdsa_public: ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFsS5Tvky/IC/dXhE/afxxUG6kdQOvdQJCYGZN42OZqWasYF+L3IG+3/wrV7jOrNrL3AyagHl6+lpPDiSXDMcpQ= root@xenial-lxd
+""" # noqa
+
+
+@pytest.mark.user_data(USER_DATA)
+class TestSshKeysProvided:
+
+ def test_ssh_dsa_keys_provided(self, class_client):
+ """Test dsa public key was imported."""
+ out = class_client.read_from_file("/etc/ssh/ssh_host_dsa_key.pub")
+ assert (
+ "AAAAB3NzaC1kc3MAAACBAPkWy1zbchVIN7qTgM0/yyY8q4R"
+ "ZS8cNM4ZpeuE5UB/Nnr6OSU/nmbO8LuM") in out
+
+ """Test dsa private key was imported."""
+ out = class_client.read_from_file("/etc/ssh/ssh_host_dsa_key")
+ assert (
+ "MIIBuwIBAAKBgQD5Fstc23IVSDe6k4DNP8smPKuEWUvHDTOGaXr"
+ "hOVAfzZ6+jklP") in out
+
+ def test_ssh_rsa_keys_provided(self, class_client):
+ """Test rsa public key was imported."""
+ out = class_client.read_from_file("/etc/ssh/ssh_host_rsa_key.pub")
+ assert (
+ "AAAAB3NzaC1yc2EAAAADAQABAAABAQC0/Ho+o3eJISydO2JvIgT"
+ "LnZOtrxPl+fSvJfKDjoOLY0HB2eOjy2s2/2N6d9X9SGZ4") in out
+
+ """Test rsa private key was imported."""
+ out = class_client.read_from_file("/etc/ssh/ssh_host_rsa_key")
+ assert (
+ "4DOkqNiUGl80Zp1RgZNohHUXlJMtAbrIlAVEk+mTmg7vjfyp2un"
+ "RQvLZpMRdywBm") in out
+
+ def test_ssh_ecdsa_keys_provided(self, class_client):
+ """Test ecdsa public key was imported."""
+ out = class_client.read_from_file("/etc/ssh/ssh_host_ecdsa_key.pub")
+ assert (
+ "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAAB"
+ "BBFsS5Tvky/IC/dXhE/afxxU") in out
+
+ """Test ecdsa private key generated."""
+ out = class_client.read_from_file("/etc/ssh/ssh_host_ecdsa_key")
+ assert (
+ "AwEHoUQDQgAEWxLlO+TL8gL91eET9p/HFQbqR1A691AkJgZk3jY"
+ "5mpZqxgX4vcgb") in out
+
+ def test_ssh_ed25519_keys_provided(self, class_client):
+ """Test ed25519 public key was imported."""
+ out = class_client.read_from_file("/etc/ssh/ssh_host_ed25519_key.pub")
+ assert (
+ "AAAAC3NzaC1lZDI1NTE5AAAAINudAZSu4vjZpVWzId5pXmZg1M6"
+ "G15dqjQ2XkNVOEnb5") in out
+
+ """Test ed25519 private key was imported."""
+ out = class_client.read_from_file("/etc/ssh/ssh_host_ed25519_key")
+ assert (
+ "XAAAAAtzc2gtZWQyNTUxOQAAACDbnQGUruL42aVVsyHeaV5mYNT"
+ "OhteXao0Nl5DVThJ2+Q") in out
diff --git a/tests/integration_tests/modules/test_timezone.py b/tests/integration_tests/modules/test_timezone.py
new file mode 100644
index 00000000..6080d79e
--- /dev/null
+++ b/tests/integration_tests/modules/test_timezone.py
@@ -0,0 +1,24 @@
+"""Integration test for the timezone module.
+
+This test specifies a timezone to be used by the ``timezone`` module
+and then checks that if that timezone was respected during boot.
+
+(This is ported from
+``tests/cloud_tests/testcases/modules/timezone.yaml``.)"""
+
+import pytest
+
+
+USER_DATA = """\
+#cloud-config
+timezone: US/Aleutian
+"""
+
+
+class TestTimezone:
+
+ @pytest.mark.user_data(USER_DATA)
+ def test_timezone(self, client):
+ timezone_output = client.execute(
+ 'date "+%Z" --date="Thu, 03 Nov 2016 00:47:00 -0400"')
+ assert timezone_output.strip() == "HDT"
diff --git a/tests/integration_tests/modules/test_write_files.py b/tests/integration_tests/modules/test_write_files.py
new file mode 100644
index 00000000..d7032a0c
--- /dev/null
+++ b/tests/integration_tests/modules/test_write_files.py
@@ -0,0 +1,65 @@
+"""Integration test for the write_files module.
+
+This test specifies files to be created by the ``write_files`` module
+and then checks if those files were created during boot.
+
+(This is ported from
+``tests/cloud_tests/testcases/modules/write_files.yaml``.)"""
+
+import base64
+import pytest
+
+
+ASCII_TEXT = "ASCII text"
+B64_CONTENT = base64.b64encode(ASCII_TEXT.encode("utf-8"))
+
+# NOTE: the binary data can be any binary data, not only executables
+# and can be generated via the base 64 command as such:
+# $ base64 < hello > hello.txt
+# the opposite is running:
+# $ base64 -d < hello.txt > hello
+#
+USER_DATA = """\
+#cloud-config
+write_files:
+- encoding: b64
+ content: {}
+ owner: root:root
+ path: /root/file_b64
+ permissions: '0644'
+- content: |
+ # My new /root/file_text
+
+ SMBDOPTIONS="-D"
+ path: /root/file_text
+- content: !!binary |
+ /Z/xrHR4WINT0UNoKPQKbuovp6+Js+JK
+ path: /root/file_binary
+ permissions: '0555'
+- encoding: gzip
+ content: !!binary |
+ H4sIAIDb/U8C/1NW1E/KzNMvzuBKTc7IV8hIzcnJVyjPL8pJ4QIA6N+MVxsAAAA=
+ path: /root/file_gzip
+ permissions: '0755'
+""".format(B64_CONTENT.decode("ascii"))
+
+
+@pytest.mark.user_data(USER_DATA)
+class TestWriteFiles:
+
+ @pytest.mark.parametrize(
+ "cmd,expected_out", (
+ ("file /root/file_b64", ASCII_TEXT),
+ ("md5sum </root/file_binary", "3801184b97bb8c6e63fa0e1eae2920d7"),
+ ("sha256sum </root/file_binary", (
+ "2c791c4037ea5bd7e928d6a87380f8ba"
+ "7a803cd83d5e4f269e28f5090f0f2c9a"
+ )),
+ ("file /root/file_gzip",
+ "POSIX shell script, ASCII text executable"),
+ ("file /root/file_text", ASCII_TEXT),
+ )
+ )
+ def test_write_files(self, cmd, expected_out, class_client):
+ out = class_client.execute(cmd)
+ assert expected_out in out