aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/source/admin/kolla_api.rst17
-rw-r--r--docker/base/set_configs.py22
-rw-r--r--releasenotes/notes/set-configs-create-directories-a1b2c3d4e5f6a7b8.yaml5
-rw-r--r--tests/test_set_config.py107
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')