summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Piper <andy.piper@arcticwolf.com>2021-11-22 10:40:37 -0500
committerAndy Piper <andy.piper@arcticwolf.com>2021-11-22 10:40:37 -0500
commit722601adb91a6532f91a7f4074a517c7f6fd6e1c (patch)
treec01b3921c7377805541425f6245336bb9fb58d85
parent470b0a05ad7717139897f08e24f48d6fedc2a8d6 (diff)
downloadvmdb2-722601adb91a6532f91a7f4074a517c7f6fd6e1c.tar.gz
Add copy_dir_plugin.py
Adds the ability to copy directory trees into the build chroot
-rw-r--r--vmdb/plugins/copy_dir.mdwn23
-rw-r--r--vmdb/plugins/copy_dir_plugin.py114
-rw-r--r--without-tests1
3 files changed, 138 insertions, 0 deletions
diff --git a/vmdb/plugins/copy_dir.mdwn b/vmdb/plugins/copy_dir.mdwn
new file mode 100644
index 0000000..a21e580
--- /dev/null
+++ b/vmdb/plugins/copy_dir.mdwn
@@ -0,0 +1,23 @@
+Step: copy-dir
+-----------------------------------------------------------------------------
+
+Recursively copy a directory from outside into the target filesystem.
+
+Step keys:
+
+* `copy-dir` &mdash; REQUIRED; the full (starting from the new
+ filesystem root) path of directory to copy to. Any missing
+ directories will be created with the configured user and group ownership and
+ permissions. See the `perm`, `uid`, `gid`, `user` and `group` keys.
+* `src` &mdash; REQUIRED; the path of the directory to copy from on the host
+ filesystem, outside the chroot, relative to the current working directory of
+ the vmdb2 process.
+* `perm` &mdash; OPTIONAL; the permissions to apply to any missing parent
+ directories that are created on the target. The value of `umask` is applied
+ to this value.
+* `umask` &mdash; OPTIONAL; the numeric (octal) representation of umask to
+ apply to the permissions of copied files and directories. Defaults to 0022.
+* `uid` &mdash; OPTIONAL; the numeric user ID to assign to the copied files
+ and directories. Defaults to 0 (root).
+* `gid` &mdash; OPTIONAL; the numeric group ID to assign to the copied files
+ and directories. Defaults to 0 (root).
diff --git a/vmdb/plugins/copy_dir_plugin.py b/vmdb/plugins/copy_dir_plugin.py
new file mode 100644
index 0000000..b6eb7d7
--- /dev/null
+++ b/vmdb/plugins/copy_dir_plugin.py
@@ -0,0 +1,114 @@
+# Copyright 2019 Gunnar Wolf
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# =*= License: GPL-3+ =*=
+
+import vmdb
+import os
+import logging
+import shutil
+
+
+class CopyDirPlugin(vmdb.Plugin):
+ def enable(self):
+ self.app.step_runners.add(CopyDirStepRunner())
+
+
+class CopyDirStepRunner(vmdb.StepRunnerInterface):
+ def get_key_spec(self):
+ return {
+ "copy-dir": str,
+ "src": str,
+ "perm": 0o755,
+ "umask": 0o022,
+ "uid": 0,
+ "gid": 0,
+ "user": "",
+ "group": ""
+ }
+
+ def run(self, values, settings, state):
+ root = state.tags.get_builder_from_target_mount_point("/")
+ newdir = values["copy-dir"]
+ src = values["src"]
+ perm = values["perm"]
+ umask = values["umask"]
+
+ if values["user"]:
+ uid = self._get_id_from_file("/".join([root, "etc/passwd"]),
+ values["user"])
+ else:
+ uid = values["uid"]
+
+ if values["group"]:
+ gid = self._get_id_from_file("/".join([root, "etc/group"]),
+ values["group"])
+ else:
+ gid = values["gid"]
+
+ tgt_dir = "/".join([root, newdir])
+
+ # ensure src and target paths don't end with a '/'
+ if src.endswith('/'):
+ src = src[:-1]
+ if tgt_dir.endswith('/'):
+ tgt_dir = tgt_dir[:-1]
+
+ vmdb.progress(
+ "Copying dir %s to %s, uid %d, gid %d, umask %o"
+ % (src, tgt_dir, uid, gid, umask)
+ )
+
+ # create missing parent dirs as required
+ tgt_parent_dir = tgt_dir
+ missing_parent_dirs = []
+ while not os.path.exists(tgt_parent_dir):
+ missing_parent_dirs.append(tgt_parent_dir)
+ tgt_parent_dir = os.path.dirname(tgt_parent_dir)
+
+ for d in reversed(missing_parent_dirs):
+ self._create_dir(d, perm & ~umask, uid, gid)
+
+ for base_dir, dirs, files in os.walk(src):
+ base_tgt = base_dir.replace(src, tgt_dir)
+ for dir in dirs:
+ src_mode = os.stat(os.path.join(base_dir, dir)).st_mode
+ umasked_mode = src_mode & ~umask
+ dir_path = os.path.join(base_tgt, dir)
+ if not os.path.exists(dir_path):
+ self._create_dir(dir_path, umasked_mode, uid, gid)
+
+ for f in files:
+ src_file = os.path.join(base_dir, f)
+ tgt_file = os.path.join(base_tgt, f)
+ src_mode = os.stat(src_file).st_mode
+ umasked_mode = src_mode & ~umask
+ with open(src_file, "rb") as inp:
+ with open(tgt_file, "wb") as output:
+ os.chown(tgt_file, uid, gid)
+ os.chmod(tgt_file, umasked_mode)
+ shutil.copyfileobj(inp, output)
+
+ def _create_dir(self, dir_path, mode, uid, gid):
+ os.makedirs(dir_path, mode=mode, exist_ok=True)
+ os.chown(dir_path, uid, gid)
+
+ def _get_id_from_file(self, file_path, name):
+ with open(file_path) as fh:
+ for line in fh:
+ if line.startswith(f"{name}:"):
+ id = line.split(':', 4)[2]
+ return int(id)
+ raise RuntimeError(f'Failed to find entry for "{name}" in {file_path}') \ No newline at end of file
diff --git a/without-tests b/without-tests
index 17ce589..50cdad4 100644
--- a/without-tests
+++ b/without-tests
@@ -5,6 +5,7 @@ vmdb/plugins/ansible_plugin.py
vmdb/plugins/apt_plugin.py
vmdb/plugins/cache_rootfs_plugin.py
vmdb/plugins/chroot_plugin.py
+vmdb/plugins/copy_dir_plugin.py
vmdb/plugins/copy_file_plugin.py
vmdb/plugins/create_dir_plugin.py
vmdb/plugins/create_file_plugin.py