1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
"""Integration tests for CLI functionality
These would be for behavior manually invoked by user from the command line
"""
import pytest
from tests.integration_tests.instances import IntegrationInstance
from tests.integration_tests.integration_settings import PLATFORM
from tests.integration_tests.releases import CURRENT_RELEASE
VALID_USER_DATA = """\
#cloud-config
runcmd:
- echo 'hi' > /var/tmp/test
"""
INVALID_USER_DATA_HEADER = """\
runcmd:
- echo 'hi' > /var/tmp/test
"""
FAILING_USER_DATA = """\
#cloud-config
bootcmd:
- exit 1
runcmd:
- exit 1
"""
# The '-' in 'hashed-password' fails schema validation
INVALID_USER_DATA_SCHEMA = """\
#cloud-config
users:
- default
- name: newsuper
gecos: Big Stuff
groups: users, admin
sudo: "ALL=(ALL) NOPASSWD:ALL"
hashed-password: asdfasdf
shell: /bin/bash
lock_passwd: true
"""
@pytest.mark.user_data(VALID_USER_DATA)
class TestValidUserData:
def test_schema_status(self, class_client: IntegrationInstance):
"""Test `cloud-init schema` with valid userdata.
PR #575
"""
result = class_client.execute("cloud-init schema --system")
if PLATFORM == "ibm":
assert "Invalid schema: vendor-data" in result.stderr
assert not result.ok
else:
assert result.ok
assert "Valid schema user-data" in result.stdout.strip()
result = class_client.execute("cloud-init status --long")
assert 0 == result.return_code, (
f"Unexpected exit {result.return_code} from cloud-init status:"
f" {result}"
)
def test_modules_init(self, class_client: IntegrationInstance):
for mode in ("init", "config", "final"):
result = class_client.execute(f"cloud-init modules --mode {mode}")
assert result.ok
assert f"'modules:{mode}'" in result.stdout.strip()
@pytest.mark.skipif(
PLATFORM == "qemu", reason="QEMU only supports #cloud-config userdata"
)
@pytest.mark.user_data(INVALID_USER_DATA_HEADER)
def test_invalid_userdata(client: IntegrationInstance):
"""Test `cloud-init schema` with invalid userdata.
PR #575
"""
result = client.execute("cloud-init schema --system")
assert not result.ok
assert "Cloud config schema errors" in result.stderr
assert (
"Expected first line to be one of: #!, ## template: jinja,"
" #cloud-boothook, #cloud-config" in result.stderr
)
result = client.execute("cloud-init status --long")
if CURRENT_RELEASE.series in ("focal", "jammy", "lunar", "mantic"):
return_code = 0 # Stable releases don't change exit code behavior
else:
return_code = 2 # 23.4 and later will exit 2 on warnings
assert (
return_code == result.return_code
), f"Unexpected exit code {result.return_code}"
@pytest.mark.user_data(INVALID_USER_DATA_SCHEMA)
def test_invalid_userdata_schema(client: IntegrationInstance):
"""Test invalid schema represented as Warnings, not fatal
PR #1175
"""
result = client.execute("cloud-init status --long")
if CURRENT_RELEASE.series in ("focal", "jammy", "lunar", "mantic"):
return_code = 0 # Stable releases don't change exit code behavior
else:
return_code = 2 # 23.4 and later will exit 2 on warnings
assert (
return_code == result.return_code
), f"Unexpected exit code {result.return_code}"
log = client.read_from_file("/var/log/cloud-init.log")
warning = (
"[WARNING]: cloud-config failed schema validation! "
"You may run 'sudo cloud-init schema --system' to check the details."
)
assert warning in log
assert "asdfasdf" not in log
@pytest.mark.user_data(FAILING_USER_DATA)
def test_failing_userdata_modules_exit_codes(client: IntegrationInstance):
"""Test failing in modules representd in exit status.
To ensure we don't miss any errors or warnings if a service happens
to be restarted, any further module invocations will exit with error
on the same boot if a previous invocation exited with error.
In this test, both bootcmd and runcmd will exit with error the first time.
The second time, runcmd will run cleanly, but still exit with error.
Since bootcmd runs in init timeframe, and runcmd runs in final timeframe,
expect error from those two modes.
"""
for mode in ("init", "config", "final"):
result = client.execute(f"cloud-init modules --mode {mode}")
assert result.ok if mode == "config" else result.failed
assert f"'modules:{mode}'" in result.stdout.strip()
|