aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.opendev.org>2025-06-23 09:02:21 +0000
committerGerrit Code Review <review@openstack.org>2025-06-23 09:02:21 +0000
commit1d2decfa42fbb888cae8140c713d8719dac1a03b (patch)
tree66db693fc3c17ee0849b7a8961478e8456408be8
parentbc018d398b5eb1fffe1f4e14baa508703df3a1a1 (diff)
parentcf3c65fef7c51b8f54c32bfa58574f58ad4071c8 (diff)
downloadkolla-1d2decfa42fbb888cae8140c713d8719dac1a03b.tar.gz
kolla-1d2decfa42fbb888cae8140c713d8719dac1a03b.zip
Merge "Fix kolla-set-configs --check to detect state mismatch"
-rw-r--r--docker/base/set_configs.py54
-rw-r--r--releasenotes/notes/bug-2114173-70d0b815a42ae239.yaml6
-rw-r--r--tests/test_set_config.py44
3 files changed, 104 insertions, 0 deletions
diff --git a/docker/base/set_configs.py b/docker/base/set_configs.py
index ab5b8c68b..80c32e507 100644
--- a/docker/base/set_configs.py
+++ b/docker/base/set_configs.py
@@ -68,6 +68,10 @@ class ConfigFileCommandDiffers(ExitingException):
pass
+class StateMismatch(ExitingException):
+ pass
+
+
class ConfigFile(object):
def __init__(self, source, dest, owner=None, perm=None, optional=False,
@@ -583,6 +587,56 @@ def execute_command_check(config):
def execute_config_check(config):
+ """Check configuration state consistency and validate config file entries.
+
+ This function compares the current config file destinations from the
+ provided config dictionary with those stored in the defaults state file.
+ If any destinations are found in the state file but not in the config,
+ a StateMismatch exception is raised. These missing files would otherwise
+ be restored or removed depending on their backup state.
+
+ After validating consistency, the function performs standard checks on
+ each declared configuration file, including content, permissions, and
+ ownership validation.
+
+ Args:
+ config (dict): The configuration dictionary containing 'config_files'
+ entries as expected by Kolla.
+
+ Raises:
+ StateMismatch: If there are entries in the defaults state not present
+ in the provided config.
+ """
+ state = get_defaults_state()
+
+ # Build a set of all current destination paths from config.json
+ # If the destination is a directory, we append the
+ # basename of the source
+ current_dests = {
+ entry['dest'] if not entry['dest'].endswith('/') else
+ os.path.join(entry['dest'], os.path.basename(entry['source']))
+ for entry in config.get('config_files', [])
+ if entry.get('dest')
+ }
+
+ # Detect any paths that are present in the state file but
+ # missing from config.json.
+ # These would be either restored (if state[dest] has a backup)
+ # or removed (if dest is null)
+ removed_dests = [
+ path for path in state.keys()
+ if path not in current_dests
+ ]
+
+ if removed_dests:
+ raise StateMismatch(
+ f"The following config files are tracked in state but missing "
+ f"from config.json. "
+ f"They would be restored or removed: {sorted(removed_dests)}"
+ )
+
+ # Perform the regular content, permissions, and ownership
+ # checks on the declared files
for data in config.get('config_files', []):
config_file = ConfigFile(**data)
config_file.check()
diff --git a/releasenotes/notes/bug-2114173-70d0b815a42ae239.yaml b/releasenotes/notes/bug-2114173-70d0b815a42ae239.yaml
new file mode 100644
index 000000000..29349be97
--- /dev/null
+++ b/releasenotes/notes/bug-2114173-70d0b815a42ae239.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+ - |
+ Fixes set_configs.py not detecting removed config files
+ during --check, which prevented container restart when
+ needed. `LP#2114173 <https://launchpad.net/bugs/2114173>`__
diff --git a/tests/test_set_config.py b/tests/test_set_config.py
index 3d4fc5a45..f29650504 100644
--- a/tests/test_set_config.py
+++ b/tests/test_set_config.py
@@ -892,3 +892,47 @@ class ConfigFileTest(base.BaseTestCase):
# Verify that the updated state was saved
mock_set_defaults_state.assert_called_once_with(expected_state)
+
+
+class ExecuteConfigCheckStateMismatchTest(base.BaseTestCase):
+
+ @mock.patch.object(set_configs, 'get_defaults_state')
+ def test_execute_config_check_raises_state_mismatch(
+ self, mock_get_defaults_state
+ ):
+ """Test execute_config_check() when state has extra config file.
+
+ This test simulates the scenario where the state file contains
+ a destination that no longer exists in config.json. It verifies:
+ - get_defaults_state() returns a state with an extra entry.
+ - execute_config_check() raises StateMismatch when config.json
+ omits a tracked destination.
+ """
+ config = {
+ "command": "/bin/true",
+ "config_files": [
+ {
+ "source": "/etc/foo/foo.conf",
+ "dest": "/etc/foo/foo.conf",
+ "owner": "user1",
+ "perm": "0644"
+ }
+ ]
+ }
+
+ mock_get_defaults_state.return_value = {
+ "/etc/foo/foo.conf": {
+ "source": "/etc/foo/foo.conf",
+ "preserve_properties": True,
+ "dest": "/etc/kolla/defaults/etc/foo/foo.conf"
+ },
+ "/etc/old/obsolete.conf": {
+ "source": "/etc/old/obsolete.conf",
+ "preserve_properties": True,
+ "dest": "/etc/kolla/defaults/etc/old/obsolete.conf"
+ }
+ }
+
+ self.assertRaises(set_configs.StateMismatch,
+ set_configs.execute_config_check,
+ config)