|
Server : Apache System : Linux server.mata-lashes.com 3.10.0-1160.90.1.el7.x86_64 #1 SMP Thu May 4 15:21:22 UTC 2023 x86_64 User : matalashes ( 1004) PHP Version : 8.1.29 Disable Function : NONE Directory : /usr/src/cloud-init/tests/unittests/config/ |
Upload File : |
# This file is part of cloud-init. See LICENSE file for license information.
import re
import tempfile
import pytest
from cloudinit import subp, util
from cloudinit.config.cc_bootcmd import handle
from cloudinit.config.schema import (
SchemaValidationError,
get_schema,
validate_cloudconfig_schema,
)
from tests.unittests.helpers import CiTestCase, mock, skipUnlessJsonSchema
from tests.unittests.util import get_cloud
class FakeExtendedTempFile:
def __init__(self, suffix):
self.suffix = suffix
self.handle = tempfile.NamedTemporaryFile(
prefix="ci-%s." % self.__class__.__name__, delete=False
)
def __enter__(self):
return self.handle
def __exit__(self, exc_type, exc_value, traceback):
self.handle.close()
util.del_file(self.handle.name)
class TestBootcmd(CiTestCase):
with_logs = True
_etmpfile_path = (
"cloudinit.config.cc_bootcmd.temp_utils.ExtendedTemporaryFile"
)
def setUp(self):
super(TestBootcmd, self).setUp()
self.subp = subp.subp
self.new_root = self.tmp_dir()
def test_handler_skip_if_no_bootcmd(self):
"""When the provided config doesn't contain bootcmd, skip it."""
cfg = {}
mycloud = get_cloud()
handle("notimportant", cfg, mycloud, None)
self.assertIn(
"Skipping module named notimportant, no 'bootcmd' key",
self.logs.getvalue(),
)
def test_handler_invalid_command_set(self):
"""Commands which can't be converted to shell will raise errors."""
invalid_config = {"bootcmd": 1}
cc = get_cloud()
with self.assertRaises(TypeError) as context_manager:
handle("cc_bootcmd", invalid_config, cc, [])
self.assertIn("Failed to shellify bootcmd", self.logs.getvalue())
self.assertEqual(
"Input to shellify was type 'int'. Expected list or tuple.",
str(context_manager.exception),
)
invalid_config = {
"bootcmd": ["ls /", 20, ["wget", "http://stuff/blah"], {"a": "n"}]
}
cc = get_cloud()
with self.assertRaises(TypeError) as context_manager:
handle("cc_bootcmd", invalid_config, cc, [])
logs = self.logs.getvalue()
self.assertIn("Failed to shellify", logs)
self.assertEqual(
"Unable to shellify type 'int'. Expected list, string, tuple. "
"Got: 20",
str(context_manager.exception),
)
def test_handler_creates_and_runs_bootcmd_script_with_instance_id(self):
"""Valid schema runs a bootcmd script with INSTANCE_ID in the env."""
cc = get_cloud()
out_file = self.tmp_path("bootcmd.out", self.new_root)
my_id = "b6ea0f59-e27d-49c6-9f87-79f19765a425"
valid_config = {
"bootcmd": ["echo {0} $INSTANCE_ID > {1}".format(my_id, out_file)]
}
with mock.patch(self._etmpfile_path, FakeExtendedTempFile):
with self.allow_subp(["/bin/sh"]):
handle("cc_bootcmd", valid_config, cc, [])
self.assertEqual(
my_id + " iid-datasource-none\n", util.load_file(out_file)
)
def test_handler_runs_bootcmd_script_with_error(self):
"""When a valid script generates an error, that error is raised."""
cc = get_cloud()
valid_config = {"bootcmd": ["exit 1"]} # Script with error
with mock.patch(self._etmpfile_path, FakeExtendedTempFile):
with self.allow_subp(["/bin/sh"]):
with self.assertRaises(subp.ProcessExecutionError) as ctxt:
handle("does-not-matter", valid_config, cc, [])
self.assertIn(
"Unexpected error while running command.\nCommand: ['/bin/sh',",
str(ctxt.exception),
)
self.assertIn(
"Failed to run bootcmd module does-not-matter",
self.logs.getvalue(),
)
@skipUnlessJsonSchema()
class TestBootCMDSchema:
"""Directly test schema rather than through handle."""
@pytest.mark.parametrize(
"config, error_msg",
(
# Valid schemas tested by meta.examples in test_schema
# Invalid schemas
(
{"bootcmd": 1},
"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"
),
),
(
{
"bootcmd": [
"ls /",
20,
["wget", "http://stuff/blah"],
{"a": "n"},
]
},
"Cloud config schema errors: bootcmd.1: 20 is not valid under"
" any of the given schemas, bootcmd.3: {'a': 'n'} is not"
" valid under any of the given schemas",
),
),
)
@skipUnlessJsonSchema()
def test_schema_validation(self, config, error_msg):
"""Assert expected schema validation and error messages."""
# New-style schema $defs exist in config/cloud-init-schema*.json
schema = get_schema()
with pytest.raises(SchemaValidationError, match=error_msg):
validate_cloudconfig_schema(config, schema, strict=True)
# vi: ts=4 expandtab