diff --git a/CHANGELOG.md b/CHANGELOG.md index edb74c22..bdc46bc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,16 +8,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added -- MPI Hook: added support for the `com.hooks.mpi.mount_dir_parent` OCI annotation. - This annotation can be used to customize the parent directory of the folder where the hook adds symlinks and mounts for dependency libraries which don't have a suitable counterpart in the container. - SSH Hook: added support for the `com.hooks.ssh.authorize_ssh_key` OCI annotation, which allows to authorize a user-provided public key for connecting to the running container. ### Changed - The configuration files for the SSH hook and the Slurm sync hook are no longer generated automatically as part of the CMake installation process. In other words, the aforementioned hooks are no longer configured and enabled by default. -- MPI Hook: dependency libraries and symlinks added by the hook in the container are now mounted or created in a dedicated directory. - Previously, the hook made mounts and created multiple duplicate links in several system directories. More details [here] (https://sarus.readthedocs.io/en/stable/config/mpi-hook.html#hook-configuration) - Updated recommended runc version to 1.1.9 - Updated CI tests from source on Fedora (36 -> 38) diff --git a/CI/src/integration_tests/test_device_access.py b/CI/src/integration_tests/test_device_access.py index 66f841f8..f2cfbcb6 100644 --- a/CI/src/integration_tests/test_device_access.py +++ b/CI/src/integration_tests/test_device_access.py @@ -158,7 +158,7 @@ def test_no_additional_permissions(self): out = util.run_command_in_container(is_centralized_repository=False, image=self.CONTAINER_IMAGE, command=container_args, - options_of_run_command=sarus_options, timeout=15) + options_of_run_command=sarus_options) assert out[0] == device_path # Restore full device access in host cgroup diff --git a/CI/src/integration_tests/test_mpi_hook.py b/CI/src/integration_tests/test_mpi_hook.py index ec655c95..8b34d49c 100755 --- a/CI/src/integration_tests/test_mpi_hook.py +++ b/CI/src/integration_tests/test_mpi_hook.py @@ -15,19 +15,6 @@ import common.util as util -def assert_annotation_overrides_default_mount_dir(image_tag, expected_libs, parent_mount_path): - try: - result = util.run_command_in_container(is_centralized_repository=False, - image="quay.io/ethcscs/sarus-integration-tests:" + image_tag, - command=["ls", parent_mount_path + "/mpi_hook"], - options_of_run_command=[ - "--mpi", "--annotation=com.hooks.mpi.mount_dir_parent=" + parent_mount_path - ]) - assert (expected_libs == set(result)) - except subprocess.CalledProcessError as _: - assert (False) - - class TestMPIHook(unittest.TestCase): """ These tests verify that the host MPI libraries are properly brought into the container. @@ -114,6 +101,7 @@ def _generate_hook_config(cls): }, "stages": ["prestart"] } + return hook_config def setUp(self): @@ -182,13 +170,6 @@ def _assert_sarus_raises_mpi_warning_containing_text(self, text, expected_occurr number_of_occurrences = sum(["[WARN]" in line and text in line for line in output.split('\n')]) assert number_of_occurrences == expected_occurrences, 'Sarus didn\'t generate the expected MPI warnings containing the text "{}".'.format(text) - def test_cli_annotation_overrides_default_mount_path(self): - parent_mount_path = "/opt/xyz" - for image_tag, libs in zip(["mpich_compatible", "mpich_compatible_symlink"], - [{'libdependency0.so', 'libdependency1.so', 'libmpi.so', 'libmpi.so.12', 'libmpich.so', 'libmpich.so.12'}, - {'libdependency0.so', 'libdependency1.so', 'libmpi.so', 'libmpi.so.12', 'libmpich.so', 'libmpich.so.12', 'libmpi.so.12.5.5'}]): - assert_annotation_overrides_default_mount_dir(image_tag, libs, parent_mount_path) - @pytest.mark.asroot class TestMPIHookDevices(unittest.TestCase): diff --git a/doc/config/mpi-hook.rst b/doc/config/mpi-hook.rst index 9e88e21e..7af40bf9 100644 --- a/doc/config/mpi-hook.rst +++ b/doc/config/mpi-hook.rst @@ -56,25 +56,8 @@ arguments, but its actions are controlled through a few environment variables: schema. * ``MPI_DEPENDENCY_LIBS``: Colon separated list of absolute paths to - libraries that are dependencies of the ``MPI_LIBS``. The hook attempts to - overlay these libraries onto corresponding libraries within the container. - However, if the version compatibility check fails due to differences in minor - versions or the inability to validate compatibility, the libraries are - mounted to an alternate location to prevent conflicts. - By default, the path of this directory in the container is ``/opt/mpi_hook``; - this location is also used for other files and symlinks which the hook may - need to create in the container. - By making use of the annotation ``com.hooks.mpi.mount_dir_parent=``, - the user can customize the parent directory of the designated mount folder for the - libraries which don't have a suitable counterpart in the container, - for example: - - .. code-block:: bash - - $ sarus run --mpi --annotation=com.hooks.mpi.mount_dir_parent= / - - When using the annotation as in the previous example, the hook constructs - the path for the mounts as ``/mpi_hook/``. + libraries that are dependencies of the ``MPI_LIBS``. These libraries + are always bind mounted in the container under ``/usr/lib``. * ``BIND_MOUNTS``: Colon separated list of absolute paths to generic files or directories that are required for the correct functionality of the diff --git a/doc/user/user_guide.rst b/doc/user/user_guide.rst index 59e731eb..b7e9bc0e 100644 --- a/doc/user/user_guide.rst +++ b/doc/user/user_guide.rst @@ -1188,25 +1188,6 @@ enabled. The ``(default)`` qualifier alongside an MPI type indicates the default MPI hook for the Sarus installation, which can be enabled just with the ``sarus run --mpi`` option. -.. note:: - - The MPI hook attempts to overlay dependency libraries (not the MPI libraries - themselves, which are always substituted in-place) onto existing - corresponding libraries within the container. - However, if the version compatibility check fails due to differences - in minor versions or the inability to validate compatibility, the libraries - will be mounted to an alternate location to prevent conflicts. - By making use of the annotation ``com.hooks.mpi.mount_dir_parent=`` - the user can configure the parent directory of the designated mount folder - for the libraries, for example: - - .. code-block:: bash - - $ sarus run --mpi --annotation=com.hooks.mpi.mount_dir_parent= / - - When using the annotation as in the previous example, the hook constructs - the path for the mounts as ``/mpi_hook/``. - .. _user-nvidia-hook: NVIDIA GPU support diff --git a/src/hooks/mpi/MpiHook.cpp b/src/hooks/mpi/MpiHook.cpp index 480bf148..88349968 100644 --- a/src/hooks/mpi/MpiHook.cpp +++ b/src/hooks/mpi/MpiHook.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include @@ -60,15 +59,6 @@ MpiHook::MpiHook() { log("Successfully initialized hook", sarus::common::LogLevel::INFO); } -static void updateLdconfigPath(const std::unordered_set& uniquePaths, const boost::filesystem::path& ldconfigFile) { - sarus::common::createFileIfNecessary(ldconfigFile.string()); - std::ofstream of{ldconfigFile.string()}; - for(auto& path: uniquePaths) { - sarus::common::Logger::getInstance().log(boost::format("Adding %s to %s") % path % ldconfigFile.string(), "MPI Hook", sarus::common::LogLevel::DEBUG); - of << path << std::endl; - } -} - void MpiHook::activateMpiSupport() { log("Activating MPI support", sarus::common::LogLevel::INFO); @@ -85,9 +75,6 @@ void MpiHook::activateMpiSupport() { injectHostLibraries(hostMpiLibs, hostToContainerMpiLibs); injectHostLibraries(hostDepLibs, hostToContainerDependencyLibs); performBindMounts(); - updateLdconfigPath({hookMountRoot.string()}, rootfsDir / "/etc/ld.so.conf.d/mpi_hook.conf"); - - log("Execute LDCONFIG", sarus::common::LogLevel::DEBUG); sarus::common::executeCommand(ldconfig.string() + " -r " + rootfsDir.string()); // update container's dynamic linker log("Successfully activated MPI support", sarus::common::LogLevel::INFO); @@ -112,14 +99,6 @@ void MpiHook::parseConfigJSONOfBundle() { gid_t gidOfUser = json["process"]["user"]["gid"].GetInt(); userIdentity = sarus::common::UserIdentity(uidOfUser, gidOfUser, {}); - if(json.HasMember("annotations")) { - if(json["annotations"].HasMember("com.hooks.mpi.mount_dir_parent")) { - hookMountRoot = boost::filesystem::path(json["annotations"]["com.hooks.mpi.mount_dir_parent"].GetString()) / "mpi_hook"; - auto message = boost::format("Mount folder for mpi_hook is set to %s") % hookMountRoot.string(); - log(message, sarus::common::LogLevel::INFO); - } - } - log("Successfully parsed bundle's config.json", sarus::common::LogLevel::INFO); } @@ -246,14 +225,15 @@ void MpiHook::checkHostContainerAbiCompatibility(const HostToContainerLibsMap& h % hostLib.getRealName() % containerLib.getRealName(); log(message, sarus::common::LogLevel::WARN); - continue; } - auto message = boost::format( - "Failed to activate MPI support. Host's MPI library %s is not ABI" - " compatible with container's MPI library %s.") - % hostLib.getRealName() - % containerLib.getRealName(); - SARUS_THROW_ERROR(message.str()); + else { + auto message = boost::format( + "Failed to activate MPI support. Host's MPI library %s is not ABI" + " compatible with container's MPI library %s.") + % hostLib.getRealName() + % containerLib.getRealName(); + SARUS_THROW_ERROR(message.str()); + } } } @@ -278,8 +258,8 @@ void MpiHook::injectHostLibrary(const SharedLibrary& hostLib, const auto it = hostToContainerLibs.find(hostLib.getPath()); if (it == hostToContainerLibs.cend()) { - log(boost::format{"no corresponding libs in container => bind mount (%s) into %s"} % hostLib.getPath() % hookMountRoot.string(), sarus::common::LogLevel::DEBUG); - auto containerLib = hookMountRoot / hostLib.getPath().filename(); + log(boost::format{"no corresponding libs in container => bind mount (%s) into /lib"} % hostLib.getPath(), sarus::common::LogLevel::DEBUG); + auto containerLib = "/lib" / hostLib.getPath().filename(); sarus::runtime::validatedBindMount(hostLib.getPath(), containerLib, userIdentity, rootfsDir); createSymlinksInDynamicLinkerDefaultSearchDirs(containerLib, hostLib.getPath().filename(), false); return; @@ -298,16 +278,17 @@ void MpiHook::injectHostLibrary(const SharedLibrary& hostLib, } else if (bestCandidateLib.isMajorAbiCompatible(hostLib)){ // risky replacement, issue warning. - log(boost::format{"WARNING: container lib (%s) is major-only-abi-compatible"} % bestCandidateLib.getPath(), sarus::common::LogLevel::DEBUG); - sarus::runtime::validatedBindMount(hostLib.getPath(), bestCandidateLib.getPath(), userIdentity, rootfsDir); - createSymlinksInDynamicLinkerDefaultSearchDirs(bestCandidateLib.getPath(), hostLib.getPath().filename(), containerHasLibsWithIncompatibleVersion); + log(boost::format{"WARNING: container lib (%s) is major-only-abi-compatible => bind mount host lib (%s) into /lib"} % bestCandidateLib.getPath() % hostLib.getPath(), sarus::common::LogLevel::DEBUG); + auto containerLib = "/lib" / hostLib.getPath().filename(); + sarus::runtime::validatedBindMount(hostLib.getPath(), containerLib, userIdentity, rootfsDir); + createSymlinksInDynamicLinkerDefaultSearchDirs(containerLib, hostLib.getPath().filename(), containerHasLibsWithIncompatibleVersion); } else { // inject with warning // NOTE: This branch is only for MPI dependency libraries. MPI libraries compatibility was already checked before at checkHostContainerAbiCompatibility. Hint for future refactoring. - log(boost::format{"WARNING: could not find ABI-compatible counterpart for host lib (%s) inside container (best candidate found: %s) => adding host lib (%s) into container's %s via bind mount "} - % hostLib.getPath() % bestCandidateLib.getPath() % hostLib.getPath() % hookMountRoot.string() , sarus::common::LogLevel::WARN); - auto containerLib = hookMountRoot / hostLib.getPath().filename(); + log(boost::format{"WARNING: could not find ABI-compatible counterpart for host lib (%s) inside container (best candidate found: %s) => adding host lib (%s) into container's /lib via bind mount "} + % hostLib.getPath() % bestCandidateLib.getPath() % hostLib.getPath(), sarus::common::LogLevel::WARN); + auto containerLib = "/lib" / hostLib.getPath().filename(); sarus::runtime::validatedBindMount(hostLib.getPath(), containerLib, userIdentity, rootfsDir); createSymlinksInDynamicLinkerDefaultSearchDirs(containerLib, hostLib.getPath().filename(), true); } @@ -346,18 +327,10 @@ void MpiHook::performBindMounts() const { log("Successfully performed bind mounts", sarus::common::LogLevel::INFO); } -boost::optional parseSharedLibAbiToSoname(const boost::filesystem::path& linkFilename) { - auto versionNumbers = sarus::common::parseSharedLibAbi(linkFilename); - if (versionNumbers.size() == 0) { - return boost::none; - } - return versionNumbers[0]; -} - void MpiHook::createSymlinksInDynamicLinkerDefaultSearchDirs(const boost::filesystem::path& target, const boost::filesystem::path& linkFilename, const bool preserveRootLink) const { - // Generate symlinks to the library in the hook mount path, to make sure that: + // Generate symlinks to the library in the container's /lib and /lib64, to make sure that: // // 1. ldconfig will find the library in the container, because the symlink will be in // one of ldconfig's default search directories. @@ -373,6 +346,10 @@ void MpiHook::createSymlinksInDynamicLinkerDefaultSearchDirs(const boost::filesy // ld.so from dynamically linking MPI applications to libmpi.so.12, if libmpi.so.12 is not // in one of the ld.so's default search paths. // + // Some ldconfig/ld.so versions/builds only search in the default directories /lib or /lib64. + // So, let's create symlinks to the library in both /lib and /lib64 to make sure that they + // will be found. + // // preserveRootLink: // As explained above, this method helps you create also a chain of symlinks that go from your library version // up to the root linkername link. (e.g. you inject libfoo.so.4.1 and you end up with links libfoo.so.4 and libfoo.so). @@ -383,10 +360,8 @@ void MpiHook::createSymlinksInDynamicLinkerDefaultSearchDirs(const boost::filesy // not the linker names, to avoid breaking the injected library for the same reason stated above. auto libName = sarus::common::getSharedLibLinkerName(linkFilename); auto linkNames = std::vector { libName.string() }; - - auto versionNumber = parseSharedLibAbiToSoname(linkFilename); - if(versionNumber) { - linkNames.push_back(linkNames.back() + "." + versionNumber.get()); + for(const auto& versionNumber : sarus::common::parseSharedLibAbi(linkFilename)) { + linkNames.push_back(linkNames.back() + "." + versionNumber); } // Preserve root links (when requested) @@ -403,32 +378,34 @@ void MpiHook::createSymlinksInDynamicLinkerDefaultSearchDirs(const boost::filesy } } - auto searchDir = rootfsDir / hookMountRoot; - sarus::common::createFoldersIfNecessary(searchDir); + // Let's create symlinks in /lib and /lib64 + auto linkerDefaultSearchDirs = std::vector {"/lib", "/lib64"}; + for (const auto& dir: linkerDefaultSearchDirs) { + auto searchDir = rootfsDir / dir; + sarus::common::createFoldersIfNecessary(searchDir); - // prevent writing as root where we are not allowed to - if (!sarus::runtime::isPathOnAllowedDevice(searchDir, rootfsDir)) { - log(boost::format("The hook is not allowed to write to %s. Ignoring symlinks creation in this path.") % searchDir, sarus::common::LogLevel::WARN); - } - - for (const auto& linkName : linkNames) { - if(boost::filesystem::path{linkName}.filename() == target.filename()) { - continue; - } - auto realLink = sarus::common::realpathWithinRootfs(rootfsDir, hookMountRoot / linkName); - auto realTarget = sarus::common::realpathWithinRootfs(rootfsDir, target); - bool linkIsTarget = (realLink == realTarget); - bool preserveLink = (linkName == libName && preserveRootLink && rootLinkExists); - if (linkIsTarget || preserveLink) { + // prevent writing as root where we are not allowed to + if (!sarus::runtime::isPathOnAllowedDevice(searchDir, rootfsDir)) { + log(boost::format("The hook is not allowed to write to %s. Ignoring symlinks creation in this path.") % searchDir, sarus::common::LogLevel::WARN); continue; } - auto link = searchDir / linkName; - boost::filesystem::remove(link); - boost::filesystem::create_symlink(target, link); - - auto message = boost::format("Created symlink in container %s -> %s") % link % target; - log(message, sarus::common::LogLevel::DEBUG); + for (const auto& linkName : linkNames) { + auto realLink = sarus::common::realpathWithinRootfs(rootfsDir, dir / linkName); + auto realTarget = sarus::common::realpathWithinRootfs(rootfsDir, target); + bool linkIsTarget = (realLink == realTarget); + bool preserveLink = (linkName == libName && preserveRootLink && rootLinkExists); + if (linkIsTarget || preserveLink) { + continue; + } + + auto link = searchDir / linkName; + boost::filesystem::remove(link); + boost::filesystem::create_symlink(target, link); + + auto message = boost::format("Created symlink in container %s -> %s") % link % target; + log(message, sarus::common::LogLevel::DEBUG); + } } } diff --git a/src/hooks/mpi/MpiHook.hpp b/src/hooks/mpi/MpiHook.hpp index 7e6c3c78..901db6d8 100644 --- a/src/hooks/mpi/MpiHook.hpp +++ b/src/hooks/mpi/MpiHook.hpp @@ -61,7 +61,6 @@ class MpiHook { private: boost::filesystem::path bundleDir; boost::filesystem::path rootfsDir; - boost::filesystem::path hookMountRoot{"/opt/mpi_hook"}; pid_t pidOfContainer; sarus::common::UserIdentity userIdentity; boost::filesystem::path ldconfig; diff --git a/src/hooks/mpi/test/Checker.hpp b/src/hooks/mpi/test/Checker.hpp index b854f966..17313134 100644 --- a/src/hooks/mpi/test/Checker.hpp +++ b/src/hooks/mpi/test/Checker.hpp @@ -75,11 +75,6 @@ class Checker { return *this; } - Checker& setCustomMountParentDir(const std::string& path) { - customMountPath = path; - return *this; - } - void checkSuccessful() const { setupTestEnvironment(); MpiHook{}.activateMpiSupport(); @@ -107,19 +102,6 @@ class Checker { void setupTestEnvironment() const { sarus::common::createFoldersIfNecessary(rootfsDir / "etc"); auto doc = test_utility::ocihooks::createBaseConfigJSON(rootfsDir, test_utility::misc::getNonRootUserIds()); - if(!customMountPath.empty()) { - rapidjson::Value jCustomMountPath(rapidjson::kObjectType); - jCustomMountPath.AddMember( - "com.hooks.mpi.mount_dir_parent", - rj::Value{customMountPath.c_str(), doc.GetAllocator()}, - doc.GetAllocator() - ); - if(!doc.HasMember("annotations")) { - doc.AddMember("annotations", jCustomMountPath, doc.GetAllocator()); - } else { - doc["annotations"] = jCustomMountPath; - } - } sarus::common::writeJSON(doc, bundleDir / "config.json"); createLibraries(); setupDynamicLinkerInContainer(); @@ -155,7 +137,6 @@ class Checker { of << dir << "\n"; } } - of << "include /etc/ld.so.conf.d/*.conf" << std::endl; of.close(); // write to disk // create /etc/ld.so.cache @@ -164,22 +145,22 @@ class Checker { void checkOnlyExpectedLibrariesAreInRootfs() const { auto expected = *expectedPostHookContainerLibs; - std::sort(expected.begin(), expected.end(), [](boost::filesystem::path first, boost::filesystem::path second) { - return first.filename().string() > second.filename().string(); - }); + for(auto& lib : expected) { + lib = rootfsDir / lib; + } + std::sort(expected.begin(), expected.end()); auto begin = boost::filesystem::recursive_directory_iterator{rootfsDir}; auto end = boost::filesystem::recursive_directory_iterator{}; auto actual = std::vector{}; std::copy_if(begin, end, std::back_inserter(actual), sarus::common::isSharedLib); - std::sort(actual.begin(), actual.end(), [](boost::filesystem::path first, boost::filesystem::path second) { - return first.filename().string() > second.filename().string(); - }); + std::sort(actual.begin(), actual.end()); - CHECK_EQUAL(actual.size(), expected.size()); - for(auto itra{actual.begin()}, itre{expected.begin()}; itra != actual.cend() && itre != expected.cend(); ++itra, ++itre) { - CHECK_EQUAL(itra->filename().string(), itre->filename().string()); - } + auto actual_s = std::string{}; + for (auto i : actual) actual_s.append(i.filename().string()); + auto expected_s = std::string{}; + for (auto i : expected) expected_s.append(i.filename().string()); + CHECK_EQUAL(expected_s, actual_s); } void checkInjectedAndPreservedLibrariesAsExpected() const { @@ -250,7 +231,6 @@ class Checker { boost::optional> expectedPostHookContainerLibs; std::vector preservedPostHookContainerLibs; std::vector bindMounts; - std::string customMountPath; }; }}}} // namespace diff --git a/src/hooks/mpi/test/test_MPIHook.cpp b/src/hooks/mpi/test/test_MPIHook.cpp index ac7b6717..477de887 100644 --- a/src/hooks/mpi/test/test_MPIHook.cpp +++ b/src/hooks/mpi/test/test_MPIHook.cpp @@ -8,6 +8,7 @@ * */ +#include "common/Logger.hpp" #include "Checker.hpp" #include "test_utility/unittest_main_function.hpp" @@ -22,96 +23,123 @@ TEST_GROUP(MPIHookTestGroup) { }; TEST(MPIHookTestGroup, test_basics) { - // no MPI libraries in host - Checker{} - .setHostMpiLibraries({}) - .setPreHookContainerLibraries({}) - .checkFailure(); - - // no MPI libraries in container - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) - .setPreHookContainerLibraries({}) - .checkFailure(); + // no MPI libraries in host + Checker{} + .setHostMpiLibraries({}) + .setPreHookContainerLibraries({}) + .checkFailure(); + + // no MPI libraries in container + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) + .setPreHookContainerLibraries({}) + .checkFailure(); } TEST(MPIHookTestGroup, test_mpi_libraries_injection) { - // MPI library in non-default linker directory - Checker{} + // MPI library in non-default linker directory + Checker{} .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) - .setPreHookContainerLibraries({"/usr/local/lib/libmpi.so.12.5.5"}) - .expectPostHookContainerLibraries( - {"/usr/local/lib/libmpi.so.12.5.5","/opt/mpi_hook/libmpi.so.12","/opt/mpi_hook/libmpi.so"}) - .checkSuccessful(); - - // multiple host and container libraries, one version of each - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12.5.5", - "/lib/libmpicxx.so.12.5.5"}) - .setPreHookContainerLibraries( - {"/lib/libmpi.so.12.5.5", "/lib/libmpicxx.so.12.5.5"}) - .expectPostHookContainerLibraries( - {"/opt/mpi_hook/libmpi.so", "/opt/mpi_hook/libmpi.so.12", "/lib/libmpi.so.12.5.5", - "/opt/mpi_hook/libmpicxx.so", "/opt/mpi_hook/libmpicxx.so.12", "/lib/libmpicxx.so.12.5.5"}) - .checkSuccessful(); - - // multiple libraries (not all in container, but all injected) + .setPreHookContainerLibraries({"/usr/local/lib/libmpi.so.12.5.5"}) + .expectPostHookContainerLibraries({ + "/usr/local/lib/libmpi.so.12.5.5", + "/lib/libmpi.so", "/lib/libmpi.so.12", "/lib/libmpi.so.12.5", "/lib/libmpi.so.12.5.5", + "/lib64/libmpi.so", "/lib64/libmpi.so.12", "/lib64/libmpi.so.12.5", "/lib64/libmpi.so.12.5.5"}) + .checkSuccessful(); + + // multiple host and container libraries, one version of each + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.5.5", "/lib/libmpicxx.so.12.5.5"}) + .setPreHookContainerLibraries({"/lib/libmpi.so.12.5.5", "/lib/libmpicxx.so.12.5.5"}) + .expectPostHookContainerLibraries({ + "/lib/libmpi.so", "/lib/libmpi.so.12", "/lib/libmpi.so.12.5", "/lib/libmpi.so.12.5.5", + "/lib64/libmpi.so", "/lib64/libmpi.so.12", "/lib64/libmpi.so.12.5", "/lib64/libmpi.so.12.5.5", + + "/lib/libmpicxx.so", "/lib/libmpicxx.so.12", "/lib/libmpicxx.so.12.5", "/lib/libmpicxx.so.12.5.5", + "/lib64/libmpicxx.so", "/lib64/libmpicxx.so.12", "/lib64/libmpicxx.so.12.5", "/lib64/libmpicxx.so.12.5.5"}) + .checkSuccessful(); + + // multiple libraries (not all in container, but all injected) // Note: we inject all the host MPI libraries also when they are not present in the container because we don't // know about the dependencies between the host's MPI libraries. E.g. libmpicxx.so might depend on libmpi.so - Checker{} + Checker{} .setHostMpiLibraries({"/lib/libmpi.so.12.5.5", "/lib/libmpicxx.so.12.5.5"}) - .setPreHookContainerLibraries({"/lib/libmpi.so.12.5.5"}) - .expectPostHookContainerLibraries( - {"/opt/mpi_hook/libmpi.so", "/opt/mpi_hook/libmpi.so.12", - "/lib/libmpi.so.12.5.5", - - "/opt/mpi_hook/libmpicxx.so", "/opt/mpi_hook/libmpicxx.so.12", - "/opt/mpi_hook/libmpicxx.so.12.5.5"}) - .checkSuccessful(); + .setPreHookContainerLibraries({"/lib/libmpi.so.12.5.5"}) + .expectPostHookContainerLibraries({ + "/lib/libmpi.so", "/lib/libmpi.so.12", "/lib/libmpi.so.12.5", "/lib/libmpi.so.12.5.5", + "/lib64/libmpi.so", "/lib64/libmpi.so.12", "/lib64/libmpi.so.12.5", "/lib64/libmpi.so.12.5.5", + + "/lib/libmpicxx.so", "/lib/libmpicxx.so.12", "/lib/libmpicxx.so.12.5", "/lib/libmpicxx.so.12.5.5", + "/lib64/libmpicxx.so", "/lib64/libmpicxx.so.12", "/lib64/libmpicxx.so.12.5", "/lib64/libmpicxx.so.12.5.5"}) + .checkSuccessful(); } TEST(MPIHookTestGroup, test_dependency_libraries_injection) { - // no libdep.so in container => create it - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12"}) - .setHostMpiDependencyLibraries({"/lib/libdep.so"}) - .setPreHookContainerLibraries({"/lib/libmpi.so.12"}) - .expectPostHookContainerLibraries({"/lib/libmpi.so.12", - "/opt/mpi_hook/libmpi.so", - "/opt/mpi_hook/libdep.so", - }) - .checkSuccessful(); - - // container's libdep.so gets replaced with host's library - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12"}) - .setHostMpiDependencyLibraries({"/lib/libdep.so"}) - .setPreHookContainerLibraries({"/lib/libmpi.so.12", "/lib/libdep.so"}) - .expectPostHookContainerLibraries({"/lib/libmpi.so.12", "/lib/libdep.so", "/opt/mpi_hook/libmpi.so" }) - .checkSuccessful(); - - // multiple dep libraries in host get all injected (libdep0.so, libdep1.so) - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12"}) - .setHostMpiDependencyLibraries({"/lib/libdep0.so", "/lib/libdep1.so"}) - .setPreHookContainerLibraries({"/lib/libmpi.so.12"}) - .expectPostHookContainerLibraries({ - "/lib/libmpi.so.12", - "/opt/mpi_hook/libmpi.so", - "/opt/mpi_hook/libdep0.so", "/opt/mpi_hook/libdep1.so" - }) + // no libdep.so in container => create it + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12"}) + .setHostMpiDependencyLibraries({"/lib/libdep.so"}) + .setPreHookContainerLibraries({"/lib/libmpi.so.12"}) + .expectPostHookContainerLibraries({ + "/lib/libmpi.so", "/lib/libmpi.so.12", + "/lib64/libmpi.so", "/lib64/libmpi.so.12", + + "/lib/libdep.so", + "/lib64/libdep.so"}) + .checkSuccessful(); + + // container's libdep.so gets replaced with host's library + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12"}) + .setHostMpiDependencyLibraries({"/lib/libdep.so"}) + .setPreHookContainerLibraries({"/lib/libmpi.so.12", "/usr/local/lib/libdep.so"}) + .expectPostHookContainerLibraries({ + "/lib/libmpi.so", "/lib/libmpi.so.12", + "/lib64/libmpi.so", "/lib64/libmpi.so.12", + + "/usr/local/lib/libdep.so", + "/lib/libdep.so", + "/lib64/libdep.so"}) .checkSuccessful(); + // multiple dep libraries in host get all injected (libdep0.so, libdep1.so) + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12"}) + .setHostMpiDependencyLibraries({"/lib/libdep0.so", "/lib/libdep1.so"}) + .setPreHookContainerLibraries({"/lib/libmpi.so.12"}) + .expectPostHookContainerLibraries({ + "/lib/libmpi.so", "/lib/libmpi.so.12", + "/lib64/libmpi.so", "/lib64/libmpi.so.12", + + "/lib/libdep0.so", + "/lib64/libdep0.so", + + "/lib/libdep1.so", + "/lib64/libdep1.so"}) + .checkSuccessful(); + + // symlinks already exist (are replaced by the hook) + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12"}) + .setHostMpiDependencyLibraries({"/lib/libdep.so"}) + .setPreHookContainerLibraries({"/lib/libmpi.so.12", "/lib/libdep.so", "/lib64/libdep.so"}) + .expectPostHookContainerLibraries({ + "/lib/libmpi.so", "/lib/libmpi.so.12", + "/lib64/libmpi.so", "/lib64/libmpi.so.12", + + "/lib/libdep.so", + "/lib64/libdep.so"}) + .checkSuccessful(); } TEST(MPIHookTestGroup, test_libraries_injection_container_version_matching) { - /* NOTES: + /* NOTES: - To properly check compatbility, the HOST and CONTAINER libraries should be defined with at least 2 of the 3 version numbers. e.g. libmpi.so.12.1 or libmpi.so.12.2.3, or even libmpi.so.12.0 not just libmpi.so.12. - This means, a CONTAINER library can be seen as OLDER, EQUAL or NEWER compared to the HOST version. e.g. for HOST version libmpi.so.12.1, CONTAINER libs libmpi.so.12.0, libmpi.so.12.1 and libmpi.so.13.1 are OLDER, EQUAL and NEWER respectively. - TESTS: + TESTS: This test checks the policy defined to handle the case when more than one library version is found in the container. Granted, it is a weird case, but it came from a real Sarus user. The container had multiple versions of a "dependecy" library (libgfortran), but we prepare the tests for both MPI and dependencies libraries. @@ -119,268 +147,314 @@ TEST(MPIHookTestGroup, test_libraries_injection_container_version_matching) { The HOST is configured to have only 1 version of each library. But the container could bring more than one. When more than one version is available in the container, we have the following possible usecases in the CONTAINER: - 0 to N older (than HOST) versions - 0 or 1 equal (as HOST) version - 0 to N newer (than HOST) versions + 0 to N older (than HOST) versions + 0 or 1 equal (as HOST) version + 0 to N newer (than HOST) versions - The HOOK injection will take this policy: - - Only one library version will be injected from the host. + The HOOK injection will take this policy: + - Only one library version will be injected from the host. - If the same (equal) is available in the container, it will be replaced. The rest of the libs in the container remain untouched. - - Otherwise the newest of the older libraries is "chosen". + - Otherwise the newest of the older libraries is "chosen". If this is ABI compatible with the host, the container library is replaced. Otherwise, the host library is injected and the container libraries remain untouched. - - Otherwise (only newer versions in container): - - A warning is printed. + - Otherwise (only newer versions in container): + - A warning is printed. - The host library is injected and both the libs and symlinks in the container remain untouched. The full chain of symlinks from linkername to lib is updated only when all container lib versions are ABI compatible with the host one. - */ - // 2 older - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12.3"}) + */ + // 2 older + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.3"}) .setPreHookContainerLibraries({"/lib/libmpi.so.12.1", "/lib/libmpi.so.12.2"}) - .expectPostHookContainerLibraries({ - "/lib/libmpi.so.12.1", "/lib/libmpi.so.12.2", - "/opt/mpi_hook/libmpi.so.12", "/opt/mpi_hook/libmpi.so" + .expectPostHookContainerLibraries({ + "/lib/libmpi.so", "/lib/libmpi.so.12", "/lib/libmpi.so.12.3", + "/lib64/libmpi.so", "/lib64/libmpi.so.12", "/lib64/libmpi.so.12.3", + + "/lib/libmpi.so.12.1", + "/lib/libmpi.so.12.2" }) .expectPreservedPostHookContainerLibraries({ "/lib/libmpi.so.12.1" }) - .checkSuccessful(); + .checkSuccessful(); - // 2 older 1 equal - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12.5"}) + // 2 older 1 equal + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.5"}) .setPreHookContainerLibraries({"/lib/libmpi.so.12.1.1", "/lib/libmpi.so.12.5", "/lib/libmpi.so.12.2.2"}) - .expectPostHookContainerLibraries({ - "/lib/libmpi.so.12.1.1", "/lib/libmpi.so.12.5", "/lib/libmpi.so.12.2.2", - "/opt/mpi_hook/libmpi.so.12", "/opt/mpi_hook/libmpi.so" - }) + .expectPostHookContainerLibraries({ + "/lib/libmpi.so", "/lib/libmpi.so.12", "/lib/libmpi.so.12.5", + "/lib64/libmpi.so", "/lib64/libmpi.so.12", "/lib64/libmpi.so.12.5", + + "/lib/libmpi.so.12.1.1", + "/lib/libmpi.so.12.2.2" + }) .expectPreservedPostHookContainerLibraries({ "/lib/libmpi.so.12.1.1", "/lib/libmpi.so.12.2.2" }) - .checkSuccessful(); + .checkSuccessful(); // NOTE: Container can't have an incompatible MPI lib (even if there's a compatible one), - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12.4"}) + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.4"}) .setPreHookContainerLibraries({"/lib/libmpi.so.12.1", "/lib64/libmpi.so.13.1"}) - .expectPostHookContainerLibraries({ - "/lib/libmpi.so.12.1", "/lib64/libmpi.so.13.1", - "/opt/mpi_hook/libmpi.so.12", "/opt/mpi_hook/libmpi.so" - }) - .checkFailure(); - // So, we continue the test with MPI dependency libs (same method is used). - - // 2 older 1 equal 2 newer - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12.1"}) - .setHostMpiDependencyLibraries({"/lib/libdep.so.4.4"}) + .expectPostHookContainerLibraries({ + "/lib/libmpi.so", "/lib/libmpi.so.12", "/lib/libmpi.so.12.4", + }) + .checkFailure(); + // So, we continue the test with MPI dependency libs (same method is used). + + // 2 older 1 equal 2 newer + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.1"}) + .setHostMpiDependencyLibraries({"/lib/libdep.so.4.4"}) .setPreHookContainerLibraries({"/lib/libmpi.so.12.1", "/lib/libdep.so.4.6", "/lib/libdep.so.4.4", "/lib/libdep.so.4.3", "/lib64/libdep.so.4.2", "/lib64/libdep.so.5.1"}) - .expectPostHookContainerLibraries({ - "/lib/libmpi.so.12.1", "/lib/libdep.so.4.6", "/lib/libdep.so.4.4", - "/lib/libdep.so.4.3", "/lib64/libdep.so.4.2", - "/lib64/libdep.so.5.1", - "/opt/mpi_hook/libmpi.so.12", "/opt/mpi_hook/libmpi.so", - "/opt/mpi_hook/libdep.so.4", "/opt/mpi_hook/libdep.so" - }) - .expectPreservedPostHookContainerLibraries({ - "/lib/libdep.so.4.6", - "/lib/libdep.so.4.3", - "/lib64/libdep.so.4.2", - "/lib64/libdep.so.5.1", - }) - .checkSuccessful(); - - // 1 equal 2 newer - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12.1"}) - .setHostMpiDependencyLibraries({"/lib/libdep.so.4.3"}) + .expectPostHookContainerLibraries({ + "/lib/libmpi.so", "/lib/libmpi.so.12", "/lib/libmpi.so.12.1", + "/lib64/libmpi.so", "/lib64/libmpi.so.12", "/lib64/libmpi.so.12.1", + + "/lib/libdep.so", "/lib/libdep.so.4", "/lib/libdep.so.4.4", + "/lib64/libdep.so", "/lib64/libdep.so.4", "/lib64/libdep.so.4.4", + + "/lib/libdep.so.4.6", + "/lib/libdep.so.4.3", + "/lib64/libdep.so.4.2", + "/lib64/libdep.so.5.1", + }) + .expectPreservedPostHookContainerLibraries({ + "/lib/libdep.so.4.6", + "/lib/libdep.so.4.3", + "/lib64/libdep.so.4.2", + "/lib64/libdep.so.5.1", + }) + .checkSuccessful(); + + // 1 equal 2 newer + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.1"}) + .setHostMpiDependencyLibraries({"/lib/libdep.so.4.3"}) .setPreHookContainerLibraries({{"/lib/libmpi.so.12.1", "/lib64/libdep.so.5.0", "/lib/libdep.so.4.3", "/lib/libdep.so.4.5"}}) - .expectPostHookContainerLibraries({ - "/lib/libmpi.so.12.1", "/lib64/libdep.so.5.0", "/lib/libdep.so.4.3", + .expectPostHookContainerLibraries({ + "/lib/libmpi.so", "/lib/libmpi.so.12", "/lib/libmpi.so.12.1", + "/lib64/libmpi.so", "/lib64/libmpi.so.12", "/lib64/libmpi.so.12.1", + + "/lib/libdep.so", "/lib/libdep.so.4", "/lib/libdep.so.4.3", + "/lib64/libdep.so", "/lib64/libdep.so.4", "/lib64/libdep.so.4.3", + + "/lib64/libdep.so.5.0", + "/lib/libdep.so.4.5", + }) + .expectPreservedPostHookContainerLibraries({ + "/lib64/libdep.so.5.0", "/lib/libdep.so.4.5", - "/opt/mpi_hook/libmpi.so.12", "/opt/mpi_hook/libmpi.so", - "/opt/mpi_hook/libdep.so.4", "/opt/mpi_hook/libdep.so" - }) - .expectPreservedPostHookContainerLibraries({ - "/lib64/libdep.so.5.0", - "/lib/libdep.so.4.5", - }) - .checkSuccessful(); - - // 2 newer - // the implementation assumes that only the major number is relevant to - // decide whenter to replace or add a new libray. Compare to the previous version, - // both /lib64/libdep.so.4.3 and /lib64/libdep.so.4.5 are ABI compatible with - // /lib/libdep.so.4.2. The hook hence mounts /lib/libdep.so.4.2 -> /lib64/libdep.so.4.3 - // (being the closer subversion) - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12.1"}) - .setHostMpiDependencyLibraries({"/lib/libdep.so.4.2"}) + }) + .checkSuccessful(); + + // 2 newer + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.1"}) + .setHostMpiDependencyLibraries({"/lib/libdep.so.4.2"}) .setPreHookContainerLibraries({"/lib/libmpi.so.12.1", "/lib64/libdep.so.4.3", "/lib64/libdep.so.4.5"}) - .expectPostHookContainerLibraries({ - "/lib/libmpi.so.12.1", "/lib64/libdep.so.4.3", "/lib64/libdep.so.4.5", - "/opt/mpi_hook/libmpi.so.12", "/opt/mpi_hook/libmpi.so", - "/opt/mpi_hook/libdep.so.4", "/opt/mpi_hook/libdep.so" - }) - .expectPreservedPostHookContainerLibraries({ - "/lib64/libdep.so.4.5", - }) - .checkSuccessful(); + .expectPostHookContainerLibraries({ + "/lib/libmpi.so", "/lib/libmpi.so.12", "/lib/libmpi.so.12.1", + "/lib64/libmpi.so", "/lib64/libmpi.so.12", "/lib64/libmpi.so.12.1", + + "/lib/libdep.so", "/lib/libdep.so.4", "/lib/libdep.so.4.2", + "/lib64/libdep.so", "/lib64/libdep.so.4", "/lib64/libdep.so.4.2", + + "/lib64/libdep.so.4.3", + "/lib64/libdep.so.4.5", + }) + .expectPreservedPostHookContainerLibraries({ + "/lib64/libdep.so.4.3", + "/lib64/libdep.so.4.5", + }) + .checkSuccessful(); +} + +TEST(MPIHookTestGroup, test_library_injection_preserves_rootlink) { + // If existing container libs are FULL ABI compatible, libdep.so can be safely overwritten + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.1"}) + .setHostMpiDependencyLibraries({"/lib/libdep.so.4.2"}) + .setPreHookContainerLibraries({"/lib/libmpi.so.12.1", "/lib64/libdep.so", "/lib64/libdep.so.4.1"}) + .expectPostHookContainerLibraries({ + "/lib/libmpi.so", "/lib/libmpi.so.12", "/lib/libmpi.so.12.1", + "/lib64/libmpi.so", "/lib64/libmpi.so.12", "/lib64/libmpi.so.12.1", + + "/lib/libdep.so", "/lib/libdep.so.4", "/lib/libdep.so.4.2", + "/lib64/libdep.so", "/lib64/libdep.so.4", "/lib64/libdep.so.4.2", + + "/lib64/libdep.so.4.1", + }) + .checkSuccessful(); + + // If existing container libs are not all FULL ABI compatible (e.g. libdep.so.5), + // libdep.so will be preserved if it exists in any of the common ld.so paths + std::vector commonPaths = {"/lib", "/lib64", "/usr/lib", "/usr/lib64"}; + // Major Incompatible + for (auto& p : commonPaths){ + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.1"}) + .setHostMpiDependencyLibraries({"/lib/libdep.so.4.2"}) + .setPreHookContainerLibraries({"/lib/libmpi.so.12.1", p/"libdep.so", "/lib64/libdep.so.5"}) + .expectPostHookContainerLibraries({ + "/lib/libmpi.so", "/lib/libmpi.so.12", "/lib/libmpi.so.12.1", + "/lib64/libmpi.so", "/lib64/libmpi.so.12", "/lib64/libmpi.so.12.1", + + "/lib/libdep.so.4", "/lib/libdep.so.4.2", + "/lib64/libdep.so.4", "/lib64/libdep.so.4.2", + + p/"libdep.so", + "/lib64/libdep.so.5", + }) + .expectPreservedPostHookContainerLibraries({ + p/"libdep.so", + "/lib64/libdep.so.5", + }) + .checkSuccessful(); + } + // Major-only Compatible + for (auto& p : commonPaths){ + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.1"}) + .setHostMpiDependencyLibraries({"/lib/libdep.so.4.2"}) + .setPreHookContainerLibraries({"/lib/libmpi.so.12.1", p/"libdep.so", "/lib64/libdep.so.4.5"}) + .expectPostHookContainerLibraries({ + "/lib/libmpi.so", "/lib/libmpi.so.12", "/lib/libmpi.so.12.1", + "/lib64/libmpi.so", "/lib64/libmpi.so.12", "/lib64/libmpi.so.12.1", + + "/lib/libdep.so.4", "/lib/libdep.so.4.2", + "/lib64/libdep.so.4", "/lib64/libdep.so.4.2", + + p/"libdep.so", + "/lib64/libdep.so.4.5", + }) + .expectPreservedPostHookContainerLibraries({ + p/"libdep.so", + "/lib64/libdep.so.4.5", + }) + .checkSuccessful(); + } } TEST(MPIHookTestGroup, test_dependency_libraries_injection_container_version_matching) { - // Reproduces webrt38418 - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12"}) - .setHostMpiDependencyLibraries({"/lib/libdep.so.4"}) + // Reproduces webrt38418 + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12"}) + .setHostMpiDependencyLibraries({"/lib/libdep.so.4"}) .setPreHookContainerLibraries({"/lib/libmpi.so.12", "/lib64/libdep.so.3", "/lib64/libdep.so.4"}) - .expectPostHookContainerLibraries({ - "/lib/libmpi.so.12", "/lib64/libdep.so.3", "/lib64/libdep.so.4", - "/opt/mpi_hook/libmpi.so", - "/opt/mpi_hook/libdep.so" - }) - .expectPreservedPostHookContainerLibraries({ - "/lib64/libdep.so.3", - }) - .checkSuccessful(); - - // Reproduces webrt38602 - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12"}) - .setHostMpiDependencyLibraries({"/lib/libdep.so.4"}) + .expectPostHookContainerLibraries({ + "/lib/libmpi.so", "/lib/libmpi.so.12", + "/lib64/libmpi.so","/lib64/libmpi.so.12", + + "/lib/libdep.so", "/lib/libdep.so.4", + "/lib64/libdep.so","/lib64/libdep.so.4", + + "/lib64/libdep.so.3", + }) + .expectPreservedPostHookContainerLibraries({ + "/lib64/libdep.so.3", + }) + .checkSuccessful(); + + // Reproduces webrt38602 + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12"}) + .setHostMpiDependencyLibraries({"/lib/libdep.so.4"}) .setPreHookContainerLibraries({"/lib/libmpi.so.12", "/lib64/libdep.so.4", "/lib64/libdep.so.5"}) - .expectPostHookContainerLibraries({ - "/lib/libmpi.so.12", "/lib64/libdep.so.4", "/lib64/libdep.so.5", - "/opt/mpi_hook/libmpi.so", - "/opt/mpi_hook/libdep.so" - }) - .expectPreservedPostHookContainerLibraries({ - "/lib64/libdep.so.5", - }) - .checkSuccessful(); + .expectPostHookContainerLibraries({ + "/lib/libmpi.so", "/lib/libmpi.so.12", + "/lib64/libmpi.so", "/lib64/libmpi.so.12", + + "/lib/libdep.so", "/lib/libdep.so.4", + "/lib64/libdep.so","/lib64/libdep.so.4", + + "/lib64/libdep.so.5", + }) + .expectPreservedPostHookContainerLibraries({ + "/lib64/libdep.so.5", + }) + .checkSuccessful(); } TEST(MPIHookTestGroup, test_bind_mounts) { - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) - .setPreHookContainerLibraries({"/lib/libmpi.so.12.5.5"}) - .setMpiBindMounts({"/dev/null", "/dev/zero", "/var/opt"}) - .checkSuccessful(); + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) + .setPreHookContainerLibraries({"/lib/libmpi.so.12.5.5"}) + .setMpiBindMounts({"/dev/null", "/dev/zero", "/var/opt"}) + .checkSuccessful(); } TEST(MPIHookTestGroup, test_abi_compatibility_check) { - // compatible libraries (same MAJOR, MINOR, PATCH) - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) - .setPreHookContainerLibraries({"/usr/lib/libmpi.so.12.5.5"}) - .checkSuccessful(); - - // compatible libraries (same MAJOR, MINOR, compatible PATCH) - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) - .setPreHookContainerLibraries({"/usr/lib/libmpi.so.12.5.0"}) - .checkSuccessful(); - - // compatible libraries (same MAJOR, MINOR, compatible PATCH) - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) - .setPreHookContainerLibraries({"/usr/lib/libmpi.so.12.5.10"}) - .checkSuccessful(); - - // compatible libraries (same MAJOR, compatible MINOR) - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) - .setPreHookContainerLibraries({"/usr/lib/libmpi.so.12.4.0"}) - .checkSuccessful(); - - // incompatible libraries (same MAJOR, incompatible MINOR) - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) - .setPreHookContainerLibraries({"/usr/lib/libmpi.so.12.6"}) - .checkSuccessful(); - - // incompatible libraries (incompatible MAJOR) - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) - .setPreHookContainerLibraries({"/usr/lib/libmpi.so.11.5.5"}) - .checkFailure(); - - // incompatible libraries (incompatible MAJOR) - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) - .setPreHookContainerLibraries({"/usr/lib/libmpi.so.13.5.5"}) - .checkFailure(); - - // impossible combatibility check (must have at least MAJOR) - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) - .setPreHookContainerLibraries({"/lib/libmpi.so"}) - .checkFailure(); - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so"}) - .setPreHookContainerLibraries({"/lib/libmpi.so.12.5.5"}) - .checkFailure(); - - // only major available (default MINOR = 0) - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12.1"}) - .setPreHookContainerLibraries({"/usr/lib/libmpi.so.12"}) - .checkSuccessful(); - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12"}) - .setPreHookContainerLibraries({"/usr/lib/libmpi.so.12.0"}) - .checkSuccessful(); - Checker{} - .setHostMpiLibraries({"/lib/libmpi.so.12"}) - .setPreHookContainerLibraries({"/usr/lib/libmpi.so.12.1"}) - .checkSuccessful(); -} + // compatible libraries (same MAJOR, MINOR, PATCH) + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) + .setPreHookContainerLibraries({"/usr/lib/libmpi.so.12.5.5"}) + .checkSuccessful(); -TEST(MPIHookTestGroup, test_mpi_libraries_injection_mounted_on_annotated_path) { - // MPI library in non-default linker directory - Checker{} - .setCustomMountParentDir("/opt/xxx") - .setHostMpiLibraries({"/lib/aarch64-linux-gnu/libmpi.so.12.5.5"}) - .setPreHookContainerLibraries({"/usr/local/lib/libmpi.so.12.5.5"}) - .expectPostHookContainerLibraries( - {"/usr/local/lib/libmpi.so.12.5.5","/opt/xxx/mpi_hook/libmpi.so.12","/opt/xxx/mpi_hook/libmpi.so"}) - .checkSuccessful(); - - // multiple host and container libraries, one version of each - Checker{} - .setCustomMountParentDir("/opt/xxx") - .setHostMpiLibraries({"/lib/aarch64-linux-gnu/libmpi.so.12.5.5", - "/lib/libmpicxx.so.12.5.5"}) - .setPreHookContainerLibraries( - {"/lib/libmpi.so.12.5.5", "/lib/libmpicxx.so.12.5.5"}) - .expectPostHookContainerLibraries( - {"/opt/xxx/mpi_hook/libmpi.so", "/opt/xxx/mpi_hook/libmpi.so.12", "/lib/libmpi.so.12.5.5", - "/opt/xxx/mpi_hook/libmpicxx.so", "/opt/xxx/mpi_hook/libmpicxx.so.12", "/lib/libmpicxx.so.12.5.5"}) - .checkSuccessful(); - - // multiple libraries (not all in container, but all injected) - // Note: we inject all the host MPI libraries also when they are not present - // in the container because we don't know about the dependencies between the - // host's MPI libraries. E.g. libmpicxx.so might depend on libmpi.so - Checker{} - .setCustomMountParentDir("/opt/xxx") - .setHostMpiLibraries( - {"/lib/libmpi.so.12.5.5", "/lib/libmpicxx.so.12.5.5"}) - .setPreHookContainerLibraries({"/lib/libmpi.so.12.5.5"}) - .expectPostHookContainerLibraries( - {"/opt/xxx/mpi_hook/libmpi.so", "/opt/xxx/mpi_hook/libmpi.so.12", - "/lib/libmpi.so.12.5.5", - - "/opt/xxx/mpi_hook/libmpicxx.so", "/opt/xxx/mpi_hook/libmpicxx.so.12", - "/opt/xxx/mpi_hook/libmpicxx.so.12.5.5"}) - .checkSuccessful(); + // compatible libraries (same MAJOR, MINOR, compatible PATCH) + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) + .setPreHookContainerLibraries({"/usr/lib/libmpi.so.12.5.0"}) + .checkSuccessful(); + // compatible libraries (same MAJOR, MINOR, compatible PATCH) + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) + .setPreHookContainerLibraries({"/usr/lib/libmpi.so.12.5.10"}) + .checkSuccessful(); -} + // compatible libraries (same MAJOR, compatible MINOR) + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) + .setPreHookContainerLibraries({"/usr/lib/libmpi.so.12.4.0"}) + .checkSuccessful(); + // incompatible libraries (same MAJOR, incompatible MINOR) + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) + .setPreHookContainerLibraries({"/usr/lib/libmpi.so.12.6"}) + .checkSuccessful(); + + // incompatible libraries (incompatible MAJOR) + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) + .setPreHookContainerLibraries({"/usr/lib/libmpi.so.11.5.5"}) + .checkFailure(); + + // incompatible libraries (incompatible MAJOR) + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) + .setPreHookContainerLibraries({"/usr/lib/libmpi.so.13.5.5"}) + .checkFailure(); + + // impossible combatibility check (must have at least MAJOR) + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.5.5"}) + .setPreHookContainerLibraries({"/lib/libmpi.so"}) + .checkFailure(); + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so"}) + .setPreHookContainerLibraries({"/lib/libmpi.so.12.5.5"}) + .checkFailure(); + + // only major available (default MINOR = 0) + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12.1"}) + .setPreHookContainerLibraries({"/usr/lib/libmpi.so.12"}) + .checkSuccessful(); + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12"}) + .setPreHookContainerLibraries({"/usr/lib/libmpi.so.12.0"}) + .checkSuccessful(); + Checker{} + .setHostMpiLibraries({"/lib/libmpi.so.12"}) + .setPreHookContainerLibraries({"/usr/lib/libmpi.so.12.1"}) + .checkSuccessful(); +} }}}} // namespace