From e263b4bde577823da9d497d1c1efa07001e44b56 Mon Sep 17 00:00:00 2001 From: Kai Lueke Date: Fri, 25 Mar 2022 14:05:00 +0100 Subject: [PATCH] Create /etc files, symlinks and folders if they don't exist The existing tmpfile logic took care of folders that the ebuild keepdir directive wanted to exist on the OS. However, files and symlinks were not created, causing them to be missing if we didn't explicitly modify the ebuild files in coreos-overlay to find a solution with tmpfiles or patching the paths to /usr. Add logic to create missing files and symlinks through tmpfile directives and preserve any directory, not only the ones with the keepdir ebuild directive. Also remove any state from the rootfs to make sure that we don't rely on it when testing our images before the release. To create the files the final /etc folder is moved to /usr/share/flatcar/etc and in the future this can be used for a better logic that could take care of updating files the user didn't modify, deleting those that aren't needed anymore, and even reconciling changed files through a 3-way merge, instead of using simple tmpfile logic. --- build_library/build_image_util.sh | 12 ++++++++ build_library/gen_tmpfiles.py | 30 +++++++++++++++---- .../changes/2022-03-30-create-etc-files.md | 1 + 3 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 changelog/changes/2022-03-30-create-etc-files.md diff --git a/build_library/build_image_util.sh b/build_library/build_image_util.sh index 7927e89b04..33cd6c20d2 100755 --- a/build_library/build_image_util.sh +++ b/build_library/build_image_util.sh @@ -658,6 +658,7 @@ finish_image() { ${tmp_ignore} "${root_fs_dir}/var" sudo "${BUILD_LIBRARY_DIR}/gen_tmpfiles.py" --root="${root_fs_dir}" \ --output="${root_fs_dir}/usr/lib/tmpfiles.d/base_image_etc.conf" \ + --files="/usr/share/flatcar" \ ${tmp_ignore} "${root_fs_dir}/etc" # Only configure bootloaders if there is a boot partition @@ -692,6 +693,17 @@ EOF fi write_contents "${root_fs_dir}" "${BUILD_DIR}/${image_contents}" + # Backup the /etc contents to /usr/share/flatcar/etc to serve as source + # for creating missing files + sudo cp -a "${root_fs_dir}/etc" "${root_fs_dir}/usr/share/flatcar/etc" + # Remove the rootfs state as it should be recreated through the + # tmpfiles and may not be present on updating machines. This + # makes sure our tests cover the case of missing files in the + # rootfs and don't rely on the new image. Not done for the developer + # container. + if [[ -n "${image_kernel}" ]]; then + sudo rm --one-file-system -rf "${root_fs_dir}/etc" "${root_fs_dir}/var" + fi # Zero all fs free space to make it more compressible so auto-update # payloads become smaller, not fatal since it won't work on linux < 3.2 diff --git a/build_library/gen_tmpfiles.py b/build_library/gen_tmpfiles.py index a4b4134ba1..3d2b4b29c5 100755 --- a/build_library/gen_tmpfiles.py +++ b/build_library/gen_tmpfiles.py @@ -19,6 +19,7 @@ def main(): parser = optparse.OptionParser(description=__doc__) parser.add_option('--root', help='Remove root prefix from output') parser.add_option('--output', help='Write output to the given file') + parser.add_option('--files', default='', help='Also works on files and symlinks and uses the given path as source for copying files') parser.add_option('--ignore', action='append', default=[], help='Ignore one or more paths (use multiple times)') opts, args = parser.parse_args() @@ -32,8 +33,11 @@ def main(): assert path.startswith(opts.root) for dirpath, dirnames, filenames in os.walk(path): - if any(f.startswith('.keep') for f in filenames): - keep.add(dirpath) + keep.add(dirpath) + if opts.files: + for f in filenames: + if not f.startswith('.keep'): + keep.add(os.path.join(dirpath, f)) # Add all parent directories too for path in frozenset(keep): @@ -61,8 +65,22 @@ def main(): if stripped in opts.ignore: continue - info = os.stat(path) - assert stat.S_ISDIR(info.st_mode) + target = '-' + info = os.stat(path, follow_symlinks=False) + if not opts.files: + assert stat.S_ISDIR(info.st_mode) + if stat.S_ISDIR(info.st_mode): + entry = 'd' + elif stat.S_ISLNK(info.st_mode): + entry = 'L' + target = os.readlink(path) + elif stat.S_ISREG(info.st_mode): + entry = 'C' + assert opts.files + target = os.path.join(opts.files, stripped.lstrip('/')) + assert target.startswith(opts.files) + else: + continue mode = stat.S_IMODE(info.st_mode) try: @@ -74,8 +92,8 @@ def main(): except KeyError: group = str(info.st_gid) - config.append('d %-22s %04o %-10s %-10s - -' - % (stripped, mode, owner, group)) + config.append('%s %-22s %04o %-10s %-10s - %s' + % (entry, stripped, mode, owner, group, target)) if opts.output: fd = open(opts.output, 'w') diff --git a/changelog/changes/2022-03-30-create-etc-files.md b/changelog/changes/2022-03-30-create-etc-files.md new file mode 100644 index 0000000000..61fb085fa3 --- /dev/null +++ b/changelog/changes/2022-03-30-create-etc-files.md @@ -0,0 +1 @@ +- Added tmpfile rules to create missing folders, files, and symlinks under `/etc` and ship with an empty rootfs to ensure that we don't rely on files the updating machines may be missing and that all needed files have a tmpfile rule. To disable the creation of a file, symlink, or folder with the default contents, create an empty file or a symlink to `/dev/null` instead of removing it. ([PR#264](https://github.com/flatcar-linux/scripts/pull/264))