[autotest] Add a new config for server-side packaging to mount a directory
With this change, user can add a config in ssp_deploy_config.json (or
ssp_deploy_shadow_config.json for local overide) to mount a directory in the
host onto a directory inside container. For example:
{
"source": "/usr/local/autotest/results/shared",
"target": "/usr/local/autotest/results/shared",
"mount": true,
"readonly": false,
"force_create": true
}
BUG=chromium:621676
TEST=local run dummy_PassServer with the new config
unittest
Change-Id: I415c22be70d39d29a8a70aabd3d9f1e64a12f2a5
Reviewed-on: https://chromium-review.googlesource.com/355181
Commit-Ready: Dan Shi <dshi@google.com>
Tested-by: Dan Shi <dshi@google.com>
Reviewed-by: Dan Shi <dshi@google.com>
diff --git a/site_utils/gs_offloader.py b/site_utils/gs_offloader.py
index 51aa245..d041cbc 100755
--- a/site_utils/gs_offloader.py
+++ b/site_utils/gs_offloader.py
@@ -377,7 +377,7 @@
sanitize_dir(dir_entry)
if DEFAULT_CTS_RESULTS_GSURI:
- upload_testresult_files(dir_entry, multiprocessing)
+ upload_testresult_files(dir_entry, multiprocessing)
if LIMIT_FILE_COUNT:
limit_file_count(dir_entry)
diff --git a/site_utils/lxc.py b/site_utils/lxc.py
index ddbdbaf..5ddb6a2 100644
--- a/site_utils/lxc.py
+++ b/site_utils/lxc.py
@@ -918,6 +918,9 @@
os.path.join(RESULT_DIR_FMT % job_folder),
False),
]
+ for mount_config in deploy_config_manager.mount_configs:
+ mount_entries.append((mount_config.source, mount_config.target,
+ mount_config.readonly))
# Update container config to mount directories.
for source, destination, readonly in mount_entries:
container.mount_dir(source, destination, readonly)
diff --git a/site_utils/lxc_config.py b/site_utils/lxc_config.py
index 5093a0c..8b02e77 100644
--- a/site_utils/lxc_config.py
+++ b/site_utils/lxc_config.py
@@ -3,10 +3,10 @@
# found in the LICENSE file.
"""
-This module helps to deploy config files from host to container. It reads
-the settings from a setting file (ssp_deploy_config), and deploy the config
-files based on the settings. The setting file has a json string of a list of
-deployment settings. For example:
+This module helps to deploy config files and shared folders from host to
+container. It reads the settings from a setting file (ssp_deploy_config), and
+deploy the config files based on the settings. The setting file has a json
+string of a list of deployment settings. For example:
[{
"source": "/etc/resolv.conf",
"target": "/etc/resolv.conf",
@@ -18,10 +18,17 @@
"target": "/root/.ssh",
"append": false,
"permission": 400
+ },
+ {
+ "source": "/usr/local/autotest/results/shared",
+ "target": "/usr/local/autotest/results/shared",
+ "mount": true,
+ "readonly": false,
+ "force_create": true
}
]
-Definition of each attribute are as follows:
+Definition of each attribute for config files are as follows:
source: config file in host to be copied to container.
target: config file's location inside container.
append: true to append the content of config file to existing file inside
@@ -29,12 +36,40 @@
be overwritten.
permission: Permission to set to the config file inside container.
-The sample settings will:
+Example:
+{
+ "source": "/etc/resolv.conf",
+ "target": "/etc/resolv.conf",
+ "append": true,
+ "permission": 400
+}
+The above example will:
1. Append the content of /etc/resolv.conf in host machine to file
/etc/resolv.conf inside container.
2. Copy all files in ssh to /root/.ssh in container.
3. Change all these files' permission to 400
+Definition of each attribute for sharing folders are as follows:
+source: a folder in host to be mounted in container.
+target: the folder's location inside container.
+mount: true to mount the source folder onto the target inside container.
+ A setting with false value of mount is invalid.
+readonly: true if the mounted folder inside container should be readonly.
+force_create: true to create the source folder if it doesn't exist.
+
+Example:
+ {
+ "source": "/usr/local/autotest/results/shared",
+ "target": "/usr/local/autotest/results/shared",
+ "mount": true,
+ "readonly": false,
+ "force_create": true
+ }
+The above example will mount folder "/usr/local/autotest/results/shared" in the
+host to path "/usr/local/autotest/results/shared" inside the container. The
+folder can be written to inside container. If the source folder doesn't exist,
+it will be created as `force_create` is set to true.
+
The setting file (ssp_deploy_config) lives in AUTOTEST_DIR folder.
For relative file path specified in ssp_deploy_config, AUTOTEST_DIR/containers
is the parent folder.
@@ -78,6 +113,9 @@
DeployConfig = collections.namedtuple(
'DeployConfig', ['source', 'target', 'append', 'permission'])
+MountConfig = collections.namedtuple(
+ 'MountConfig', ['source', 'target', 'mount', 'readonly',
+ 'force_create'])
class SSPDeployError(Exception):
@@ -98,6 +136,31 @@
"""
@staticmethod
+ def validate_path(deploy_config):
+ """Validate the source and target in deploy_config dict.
+
+ @param deploy_config: A dictionary of deploy config to be validated.
+
+ @raise SSPDeployError: If any path in deploy config is invalid.
+ """
+ target = deploy_config['target']
+ source = deploy_config['source']
+ if not os.path.isabs(target):
+ raise SSPDeployError('Target path must be absolute path: %s' %
+ target)
+ if not os.path.isabs(source):
+ if source.startswith('~'):
+ # This is to handle the case that the script is run with sudo.
+ inject_user_path = ('~%s%s' % (utils.get_real_user(),
+ source[1:]))
+ source = os.path.expanduser(inject_user_path)
+ else:
+ source = os.path.join(common.autotest_dir, source)
+ # Update the source setting in deploy config with the updated path.
+ deploy_config['source'] = source
+
+
+ @staticmethod
def validate(deploy_config):
"""Validate the deploy config.
@@ -112,23 +175,35 @@
@raise SSPDeployError: If the deploy config is invalid.
"""
- c = DeployConfig(**deploy_config)
- if not os.path.isabs(c.target):
- raise SSPDeployError('Target path must be absolute path: %s' %
- c.target)
- if not os.path.isabs(c.source):
- if c.source.startswith('~'):
- # This is to handle the case that the script is run with sudo.
- inject_user_path = ('~%s%s' % (utils.get_real_user(),
- c.source[1:]))
- source = os.path.expanduser(inject_user_path)
- else:
- source = os.path.join(common.autotest_dir, c.source)
- deploy_config['source'] = source
-
+ DeployConfigManager.validate_path(deploy_config)
return DeployConfig(**deploy_config)
+ @staticmethod
+ def validate_mount(deploy_config):
+ """Validate the deploy config for mounting a directory.
+
+ Deploy configs need to be validated and pre-processed, e.g.,
+ 1. Target must be an absolute path.
+ 2. Source must be updated to be an absolute path.
+ 3. Mount must be true.
+
+ @param deploy_config: A dictionary of deploy config to be validated.
+
+ @return: A DeployConfig object that contains the deploy config.
+
+ @raise SSPDeployError: If the deploy config is invalid.
+
+ """
+ DeployConfigManager.validate_path(deploy_config)
+ c = MountConfig(**deploy_config)
+ if not c.mount:
+ raise SSPDeployError('`mount` must be true.')
+ if not c.force_create and not os.path.exists(c.source):
+ raise SSPDeployError('`source` does not exist.')
+ return c
+
+
def __init__(self, container):
"""Initialize the deploy config manager.
@@ -145,7 +220,10 @@
else SSP_DEPLOY_CONFIG_FILE)
with open(config_file) as f:
deploy_configs = json.load(f)
- self.deploy_configs = [self.validate(c) for c in deploy_configs]
+ self.deploy_configs = [self.validate(c) for c in deploy_configs
+ if 'append' in c]
+ self.mount_configs = [self.validate_mount(c) for c in deploy_configs
+ if 'mount' in c]
self.tmp_append = os.path.join(self.container.rootfs, APPEND_FOLDER)
if lxc_utils.path_exists(self.tmp_append):
utils.run('sudo rm -rf "%s"' % self.tmp_append)
@@ -310,6 +388,10 @@
"""
for deploy_config in self.deploy_configs:
self._deploy_config_pre_start(deploy_config)
+ for mount_config in self.mount_configs:
+ if (mount_config.force_create and
+ not os.path.exists(mount_config.source)):
+ utils.run('mkdir -p %s' % mount_config.source)
def deploy_post_start(self):