diff options
| author | Michal Nasiadka <mnasiadka@gmail.com> | 2026-04-27 15:28:17 +0200 |
|---|---|---|
| committer | Michal Nasiadka <mnasiadka@gmail.com> | 2026-05-05 10:34:26 +0000 |
| commit | 33c0e26e8a0290f0b6d647bb67e18a4f500907fc (patch) | |
| tree | d87fa00b1bb4c60e664111bd895bf629c903e060 | |
| parent | 1627e5da6d6ac754be3e0afe61eeddf6e38f93b6 (diff) | |
| download | kolla-33c0e26e8a0290f0b6d647bb67e18a4f500907fc.tar.gz kolla-33c0e26e8a0290f0b6d647bb67e18a4f500907fc.zip | |
set_configs: Add support for creating directories and setting perms
Change-Id: Id63c035fafaa68a9004c65161c5a3a3ad5544f02
Signed-off-by: Michal Nasiadka <mnasiadka@gmail.com>
| -rw-r--r-- | doc/source/admin/kolla_api.rst | 17 | ||||
| -rw-r--r-- | docker/base/set_configs.py | 22 | ||||
| -rw-r--r-- | releasenotes/notes/set-configs-create-directories-a1b2c3d4e5f6a7b8.yaml | 5 | ||||
| -rw-r--r-- | tests/test_set_config.py | 107 |
4 files changed, 151 insertions, 0 deletions
diff --git a/doc/source/admin/kolla_api.rst b/doc/source/admin/kolla_api.rst index 2d0a17a0f..b1fd6b140 100644 --- a/doc/source/admin/kolla_api.rst +++ b/doc/source/admin/kolla_api.rst @@ -54,6 +54,16 @@ The `kolla_set_configs`_ script understands the following attributes: * **merge**: merges the source directory into the target directory instead of replacing it. Boolean, defaults to ``false``. +* **directories**: create directories inside the container if they do not + exist, and set their ownership and permissions. A list of dicts, each + containing the following attributes: + + * **path** (required): the path to the directory to create. + * **owner** (required): the ``user:group`` to change ownership to. ``user`` + is synonymous to ``user:user``. Must be user and group names, not uid/gid. + * **perm** (required): the unix permissions to set to the directory. + Must be passed in the numeric octal form. + * **permissions**: change the permissions and/or ownership of files or directories inside the container. A list of dicts, each containing the following attributes: @@ -84,6 +94,13 @@ Here is an example configuration file: "optional": false } ], + "directories": [ + { + "path": "/var/lib/trove", + "owner": "trove", + "perm": "0755" + } + ], "permissions": [ { "path": "/var/log/kolla/trove", diff --git a/docker/base/set_configs.py b/docker/base/set_configs.py index 80c32e507..1a80bd877 100644 --- a/docker/base/set_configs.py +++ b/docker/base/set_configs.py @@ -257,6 +257,7 @@ class ConfigFile(object): def validate_config(config): required_keys = {'source', 'dest'} + required_dir_keys = {'path', 'owner', 'perm'} if 'command' not in config: raise InvalidConfig('Config is missing required "command" key') @@ -272,6 +273,12 @@ def validate_config(config): raise InvalidConfig( 'Config needs preserve_properties or owner and perm') + for data in config.get('directories', list()): + if not set(data.keys()) >= required_dir_keys: + message = ('directories config is missing required keys: %s' + % (required_dir_keys - set(data.keys()))) + raise InvalidConfig(message) + def validate_source(data): source = data.get('source') @@ -334,6 +341,19 @@ def copy_config(config): LOG.exception('Failed to set permission of %s to 0o644', cmd) +def create_directories(config): + if 'directories' not in config: + return + LOG.info('Creating directories') + for data in config['directories']: + path = data['path'] + try: + os.makedirs(path, exist_ok=True) + except Exception: + raise OSError("Can't create destination directory (%s)" % (path)) + handle_permissions({'permissions': [data]}) + + def user_group(owner): if ':' in owner: user, group = owner.split(':', 1) @@ -560,6 +580,7 @@ def execute_config_strategy(config): config_strategy = os.environ.get("KOLLA_CONFIG_STRATEGY") LOG.info("Kolla config strategy set to: %s", config_strategy) if config_strategy == "COPY_ALWAYS": + create_directories(config) handle_defaults(config) copy_config(config) handle_permissions(config) @@ -569,6 +590,7 @@ def execute_config_strategy(config): "The config strategy prevents copying new configs", exit_code=0) else: + create_directories(config) handle_defaults(config) copy_config(config) handle_permissions(config) diff --git a/releasenotes/notes/set-configs-create-directories-a1b2c3d4e5f6a7b8.yaml b/releasenotes/notes/set-configs-create-directories-a1b2c3d4e5f6a7b8.yaml new file mode 100644 index 000000000..4ffba3b10 --- /dev/null +++ b/releasenotes/notes/set-configs-create-directories-a1b2c3d4e5f6a7b8.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Adds support for a ``directories`` section in ``config.json`` processed by + ``kolla_set_configs``. diff --git a/tests/test_set_config.py b/tests/test_set_config.py index f29650504..6e3ddf5e3 100644 --- a/tests/test_set_config.py +++ b/tests/test_set_config.py @@ -894,6 +894,113 @@ class ConfigFileTest(base.BaseTestCase): mock_set_defaults_state.assert_called_once_with(expected_state) +class ValidateConfigDirectoriesTest(base.BaseTestCase): + + def test_validate_config_directories_missing_path(self): + config = { + 'command': '/bin/true', + 'directories': [{'owner': 'myuser', 'perm': '0755'}] + } + self.assertRaises(set_configs.InvalidConfig, + set_configs.validate_config, config) + + def test_validate_config_directories_missing_owner(self): + config = { + 'command': '/bin/true', + 'directories': [{'path': '/var/log/foo', 'perm': '0755'}] + } + self.assertRaises(set_configs.InvalidConfig, + set_configs.validate_config, config) + + def test_validate_config_directories_missing_perm(self): + config = { + 'command': '/bin/true', + 'directories': [{'path': '/var/log/foo', 'owner': 'myuser'}] + } + self.assertRaises(set_configs.InvalidConfig, + set_configs.validate_config, config) + + def test_validate_config_directories_valid(self): + config = { + 'command': '/bin/true', + 'directories': [ + {'path': '/var/log/foo', 'owner': 'myuser', 'perm': '0755'} + ] + } + set_configs.validate_config(config) + + +class CreateDirectoriesTest(base.BaseTestCase): + + @mock.patch.object(set_configs, 'handle_permissions') + @mock.patch('os.makedirs') + @mock.patch('os.path.exists', return_value=False) + def test_create_directory_not_exists(self, mock_exists, mock_makedirs, + mock_handle_permissions): + config = { + 'directories': [ + {'path': '/var/log/foo', 'owner': 'myuser', 'perm': '0755'} + ] + } + set_configs.create_directories(config) + + mock_makedirs.assert_called_once_with('/var/log/foo', exist_ok=True) + mock_handle_permissions.assert_called_once_with( + {'permissions': [ + {'path': '/var/log/foo', 'owner': 'myuser', 'perm': '0755'} + ]} + ) + + @mock.patch.object(set_configs, 'handle_permissions') + @mock.patch('os.makedirs') + @mock.patch('os.path.exists', return_value=True) + def test_create_directory_already_exists(self, mock_exists, mock_makedirs, + mock_handle_permissions): + config = { + 'directories': [ + {'path': '/var/log/foo', 'owner': 'myuser', 'perm': '0755'} + ] + } + set_configs.create_directories(config) + + mock_makedirs.assert_called_once_with('/var/log/foo', exist_ok=True) + mock_handle_permissions.assert_called_once_with( + {'permissions': [ + {'path': '/var/log/foo', 'owner': 'myuser', 'perm': '0755'} + ]} + ) + + @mock.patch.object(set_configs, 'handle_permissions') + @mock.patch('os.makedirs') + @mock.patch('os.path.exists', return_value=False) + def test_create_directories_no_directories_key( + self, mock_exists, mock_makedirs, mock_handle_permissions): + config = {'command': '/bin/true'} + set_configs.create_directories(config) + + mock_exists.assert_not_called() + mock_makedirs.assert_not_called() + mock_handle_permissions.assert_not_called() + + @mock.patch.object(set_configs, 'handle_permissions') + @mock.patch('os.makedirs') + @mock.patch('os.path.exists', return_value=False) + def test_create_multiple_directories(self, mock_exists, mock_makedirs, + mock_handle_permissions): + config = { + 'directories': [ + {'path': '/var/log/foo', 'owner': 'user1', 'perm': '0755'}, + {'path': '/var/log/bar', 'owner': 'user2', 'perm': '0750'}, + ] + } + set_configs.create_directories(config) + + self.assertEqual(mock_makedirs.call_args_list, + [mock.call('/var/log/foo', exist_ok=True), + mock.call('/var/log/bar', exist_ok=True)]) + self.assertEqual(mock_handle_permissions.call_count, 2) + + class ExecuteConfigCheckStateMismatchTest(base.BaseTestCase): @mock.patch.object(set_configs, 'get_defaults_state') |
