diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/other_tools/just_mr/TARGETS | 2 | ||||
-rw-r--r-- | src/other_tools/just_mr/setup.cpp | 18 | ||||
-rw-r--r-- | src/other_tools/ops_maps/TARGETS | 27 | ||||
-rw-r--r-- | src/other_tools/ops_maps/git_tree_fetch_map.cpp | 406 | ||||
-rw-r--r-- | src/other_tools/ops_maps/git_tree_fetch_map.hpp | 64 | ||||
-rw-r--r-- | src/other_tools/repo_map/TARGETS | 2 | ||||
-rw-r--r-- | src/other_tools/repo_map/repos_to_setup_map.cpp | 10 | ||||
-rw-r--r-- | src/other_tools/root_maps/TARGETS | 16 | ||||
-rw-r--r-- | src/other_tools/root_maps/tree_id_git_map.cpp | 462 | ||||
-rw-r--r-- | src/other_tools/root_maps/tree_id_git_map.hpp | 26 |
10 files changed, 555 insertions, 478 deletions
diff --git a/src/other_tools/just_mr/TARGETS b/src/other_tools/just_mr/TARGETS index 391a8240..8973026a 100644 --- a/src/other_tools/just_mr/TARGETS +++ b/src/other_tools/just_mr/TARGETS @@ -153,7 +153,9 @@ , ["src/other_tools/just_mr/progress_reporting", "progress"] , ["src/other_tools/just_mr/progress_reporting", "progress_reporter"] , "utils" + , ["src/other_tools/ops_maps", "content_cas_map"] , ["src/other_tools/ops_maps", "critical_git_op_map"] + , ["src/other_tools/ops_maps", "git_tree_fetch_map"] , ["src/other_tools/repo_map", "repos_to_setup_map"] , ["src/other_tools/root_maps", "commit_git_map"] , ["src/other_tools/root_maps", "content_git_map"] diff --git a/src/other_tools/just_mr/setup.cpp b/src/other_tools/just_mr/setup.cpp index 6cd9462b..b82993ab 100644 --- a/src/other_tools/just_mr/setup.cpp +++ b/src/other_tools/just_mr/setup.cpp @@ -29,7 +29,9 @@ #include "src/other_tools/just_mr/progress_reporting/progress_reporter.hpp" #include "src/other_tools/just_mr/setup_utils.hpp" #include "src/other_tools/just_mr/utils.hpp" +#include "src/other_tools/ops_maps/content_cas_map.hpp" #include "src/other_tools/ops_maps/critical_git_op_map.hpp" +#include "src/other_tools/ops_maps/git_tree_fetch_map.hpp" #include "src/other_tools/repo_map/repos_to_setup_map.hpp" #include "src/other_tools/root_maps/commit_git_map.hpp" #include "src/other_tools/root_maps/content_git_map.hpp" @@ -115,6 +117,14 @@ auto MultiRepoSetup(std::shared_ptr<Configuration> const& config, common_args.git_path->string(), *common_args.local_launcher, common_args.jobs); + auto git_tree_fetch_map = + CreateGitTreeFetchMap(&critical_git_op_map, + &import_to_git_map, + common_args.git_path->string(), + *common_args.local_launcher, + local_api ? &(*local_api) : nullptr, + remote_api ? &(*remote_api) : nullptr, + common_args.jobs); auto resolve_symlinks_map = CreateResolveSymlinksMap(); auto commit_git_map = @@ -152,13 +162,7 @@ auto MultiRepoSetup(std::shared_ptr<Configuration> const& config, &critical_git_op_map, common_args.jobs); auto tree_id_git_map = - CreateTreeIdGitMap(&critical_git_op_map, - &import_to_git_map, - common_args.git_path->string(), - *common_args.local_launcher, - local_api ? &(*local_api) : nullptr, - remote_api ? &(*remote_api) : nullptr, - common_args.jobs); + CreateTreeIdGitMap(&git_tree_fetch_map, common_args.jobs); auto repos_to_setup_map = CreateReposToSetupMap(config, main, interactive, diff --git a/src/other_tools/ops_maps/TARGETS b/src/other_tools/ops_maps/TARGETS index c0f05d63..f77ac3e9 100644 --- a/src/other_tools/ops_maps/TARGETS +++ b/src/other_tools/ops_maps/TARGETS @@ -92,4 +92,31 @@ , ["src/other_tools/just_mr/progress_reporting", "progress"] ] } +, "git_tree_fetch_map": + { "type": ["@", "rules", "CC", "library"] + , "name": ["git_tree_fetch_map"] + , "hdrs": ["git_tree_fetch_map.hpp"] + , "srcs": ["git_tree_fetch_map.cpp"] + , "deps": + [ ["@", "json", "", "json"] + , ["src/buildtool/execution_api/common", "common"] + , ["src/other_tools/ops_maps", "critical_git_op_map"] + , ["src/other_tools/ops_maps", "import_to_git_map"] + ] + , "stage": ["src", "other_tools", "ops_maps"] + , "private-deps": + [ ["src/other_tools/ops_maps", "critical_git_op_map"] + , ["src/buildtool/execution_api/common", "common"] + , ["src/buildtool/file_system", "file_system_manager"] + , ["src/buildtool/storage", "config"] + , ["src/buildtool/storage", "fs_utils"] + , ["src/buildtool/system", "system_command"] + , ["src/other_tools/git_operations", "git_repo_remote"] + , ["src/other_tools/just_mr/progress_reporting", "progress"] + , ["src/other_tools/just_mr/progress_reporting", "statistics"] + , ["src/other_tools/ops_maps", "content_cas_map"] + , ["src/other_tools/ops_maps", "import_to_git_map"] + , ["@", "fmt", "", "fmt"] + ] + } } diff --git a/src/other_tools/ops_maps/git_tree_fetch_map.cpp b/src/other_tools/ops_maps/git_tree_fetch_map.cpp new file mode 100644 index 00000000..e928fb2f --- /dev/null +++ b/src/other_tools/ops_maps/git_tree_fetch_map.cpp @@ -0,0 +1,406 @@ +// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/other_tools/ops_maps/git_tree_fetch_map.hpp" + +#include <cstdlib> + +#include "fmt/core.h" +#include "src/buildtool/execution_api/common/execution_common.hpp" +#include "src/buildtool/file_system/file_system_manager.hpp" +#include "src/buildtool/storage/config.hpp" +#include "src/buildtool/storage/fs_utils.hpp" +#include "src/buildtool/system/system_command.hpp" +#include "src/other_tools/git_operations/git_repo_remote.hpp" +#include "src/other_tools/just_mr/progress_reporting/progress.hpp" +#include "src/other_tools/just_mr/progress_reporting/statistics.hpp" + +auto CreateGitTreeFetchMap( + gsl::not_null<CriticalGitOpMap*> const& critical_git_op_map, + gsl::not_null<ImportToGitMap*> const& import_to_git_map, + std::string const& git_bin, + std::vector<std::string> const& launcher, + IExecutionApi* local_api, + IExecutionApi* remote_api, + std::size_t jobs) -> GitTreeFetchMap { + auto tree_to_cache = [critical_git_op_map, + import_to_git_map, + git_bin, + launcher, + local_api, + remote_api](auto ts, + auto setter, + auto logger, + auto /*unused*/, + auto const& key) { + // check whether tree exists already in Git cache; + // ensure Git cache exists + GitOpKey op_key = {.params = + { + StorageConfig::GitRoot(), // target_path + "", // git_hash + "", // branch + std::nullopt, // message + true // init_bare + }, + .op_type = GitOpType::ENSURE_INIT}; + critical_git_op_map->ConsumeAfterKeysReady( + ts, + {std::move(op_key)}, + [critical_git_op_map, + import_to_git_map, + git_bin, + launcher, + local_api, + remote_api, + key, + ts, + setter, + logger](auto const& values) { + GitOpValue op_result = *values[0]; + // check flag + if (not op_result.result) { + (*logger)("Git cache init failed", + /*fatal=*/true); + return; + } + // Open fake tmp repo to check if tree is known to Git cache + auto git_repo = GitRepoRemote::Open( + op_result.git_cas); // link fake repo to odb + if (not git_repo) { + (*logger)(fmt::format("Could not open repository {}", + StorageConfig::GitRoot().string()), + /*fatal=*/true); + return; + } + // setup wrapped logger + auto wrapped_logger = std::make_shared<AsyncMapConsumerLogger>( + [logger](auto const& msg, bool fatal) { + (*logger)(fmt::format("While checking tree exists in " + "Git cache:\n{}", + msg), + fatal); + }); + // check if the desired tree ID is in Git cache + auto tree_found = + git_repo->CheckTreeExists(key.hash, wrapped_logger); + if (not tree_found) { + // errors encountered + return; + } + if (*tree_found) { + (*setter)(true /*cache hit*/); + return; + } + JustMRProgress::Instance().TaskTracker().Start(key.origin); + // check if tree is in remote CAS, if a remote is given + auto digest = ArtifactDigest{key.hash, 0, /*is_tree=*/true}; + if (remote_api != nullptr and local_api != nullptr and + remote_api->IsAvailable(digest) and + remote_api->RetrieveToCas( + {Artifact::ObjectInfo{.digest = digest, + .type = ObjectType::Tree}}, + local_api)) { + JustMRProgress::Instance().TaskTracker().Stop(key.origin); + // Move tree from CAS to local Git storage + auto tmp_dir = StorageUtils::CreateTypedTmpDir( + "fetch-remote-git-tree"); + if (not tmp_dir) { + (*logger)(fmt::format("Failed to create tmp " + "directory for copying " + "git-tree {} from remote CAS", + key.hash), + true); + return; + } + if (not local_api->RetrieveToPaths( + {Artifact::ObjectInfo{.digest = digest, + .type = ObjectType::Tree}}, + {tmp_dir->GetPath()})) { + (*logger)( + fmt::format("Failed to copy git-tree {} to {}", + key.hash, + tmp_dir->GetPath().string()), + true); + return; + } + CommitInfo c_info{tmp_dir->GetPath(), "tree", key.hash}; + import_to_git_map->ConsumeAfterKeysReady( + ts, + {std::move(c_info)}, + [tmp_dir, // keep tmp_dir alive + key, + setter, + logger](auto const& values) { + if (not values[0]->second) { + (*logger)("Importing to git failed", + /*fatal=*/true); + return; + } + // success + (*setter)(false /*no cache hit*/); + }, + [logger, tmp_dir, tree_id = key.hash](auto const& msg, + bool fatal) { + (*logger)( + fmt::format("While moving git-tree {} from " + "{} to local git:\n{}", + tree_id, + tmp_dir->GetPath().string(), + msg), + fatal); + }); + + return; + } + // create temporary location for command execution root + auto tmp_dir = StorageUtils::CreateTypedTmpDir("git-tree"); + if (not tmp_dir) { + (*logger)("Failed to create tmp directory for tree id map!", + /*fatal=*/true); + return; + } + // create temporary location for storing command result files + auto out_dir = StorageUtils::CreateTypedTmpDir("git-tree"); + if (not out_dir) { + (*logger)("Failed to create tmp directory for tree id map!", + /*fatal=*/true); + return; + } + // execute command in temporary location + SystemCommand system{key.hash}; + auto cmdline = launcher; + std::copy(key.command.begin(), + key.command.end(), + std::back_inserter(cmdline)); + std::map<std::string, std::string> env{key.env_vars}; + for (auto const& k : key.inherit_env) { + const char* v = std::getenv(k.c_str()); + if (v != nullptr) { + env[k] = std::string(v); + } + } + auto const command_output = system.Execute( + cmdline, env, tmp_dir->GetPath(), out_dir->GetPath()); + if (not command_output) { + (*logger)(fmt::format("Failed to execute command:\n{}", + nlohmann::json(cmdline).dump()), + /*fatal=*/true); + return; + } + // do an import to git with tree check + GitOpKey op_key = {.params = + { + tmp_dir->GetPath(), // target_path + "", // git_hash + "", // branch + fmt::format("Content of tree {}", + key.hash), // message + }, + .op_type = GitOpType::INITIAL_COMMIT}; + critical_git_op_map->ConsumeAfterKeysReady( + ts, + {std::move(op_key)}, + [tmp_dir, // keep tmp_dir alive + out_dir, // keep stdout/stderr of command alive + critical_git_op_map, + just_git_cas = op_result.git_cas, + cmdline, + command_output, + key, + git_bin, + launcher, + ts, + setter, + logger](auto const& values) { + GitOpValue op_result = *values[0]; + // check flag + if (not op_result.result) { + (*logger)("Commit failed", + /*fatal=*/true); + return; + } + // Open fake tmp repository to check for tree + auto git_repo = GitRepoRemote::Open( + op_result.git_cas); // link fake repo to odb + if (not git_repo) { + (*logger)( + fmt::format("Could not open repository {}", + tmp_dir->GetPath().string()), + /*fatal=*/true); + return; + } + // setup wrapped logger + auto wrapped_logger = + std::make_shared<AsyncMapConsumerLogger>( + [logger](auto const& msg, bool fatal) { + (*logger)(fmt::format("While checking tree " + "exists:\n{}", + msg), + fatal); + }); + // check that the desired tree ID is part of the repo + auto tree_check = + git_repo->CheckTreeExists(key.hash, wrapped_logger); + if (not tree_check) { + // errors encountered + return; + } + if (not *tree_check) { + std::string out_str{}; + std::string err_str{}; + auto cmd_out = FileSystemManager::ReadFile( + command_output->stdout_file); + auto cmd_err = FileSystemManager::ReadFile( + command_output->stderr_file); + if (cmd_out) { + out_str = *cmd_out; + } + if (cmd_err) { + err_str = *cmd_err; + } + std::string output{}; + if (!out_str.empty() || !err_str.empty()) { + output = + fmt::format(".\nOutput of command:\n{}{}", + out_str, + err_str); + } + (*logger)( + fmt::format("Executing {} did not create " + "specified tree {}{}", + nlohmann::json(cmdline).dump(), + key.hash, + output), + /*fatal=*/true); + return; + } + auto target_path = tmp_dir->GetPath(); + // fetch all into Git cache + auto just_git_repo = GitRepoRemote::Open(just_git_cas); + if (not just_git_repo) { + (*logger)(fmt::format("Could not open Git " + "repository {}", + target_path.string()), + /*fatal=*/true); + return; + } + // define temp repo path + auto tmp_dir = + StorageUtils::CreateTypedTmpDir("git-tree"); + ; + if (not tmp_dir) { + (*logger)(fmt::format("Could not create unique " + "path for target {}", + target_path.string()), + /*fatal=*/true); + return; + } + wrapped_logger = + std::make_shared<AsyncMapConsumerLogger>( + [logger, target_path](auto const& msg, + bool fatal) { + (*logger)( + fmt::format("While fetch via tmp repo " + "for target {}:\n{}", + target_path.string(), + msg), + fatal); + }); + if (not just_git_repo->FetchViaTmpRepo( + tmp_dir->GetPath(), + target_path.string(), + std::nullopt, + git_bin, + launcher, + wrapped_logger)) { + return; + } + // setup a wrapped_logger + wrapped_logger = + std::make_shared<AsyncMapConsumerLogger>( + [logger, target_path](auto const& msg, + bool fatal) { + (*logger)( + fmt::format("While doing keep commit " + "and setting Git tree for " + "target {}:\n{}", + target_path.string(), + msg), + fatal); + }); + // Keep tag for commit + GitOpKey op_key = { + .params = + { + StorageConfig::GitRoot(), // target_path + *op_result.result, // git_hash + "", // branch + "Keep referenced tree alive" // message + }, + .op_type = GitOpType::KEEP_TAG}; + critical_git_op_map->ConsumeAfterKeysReady( + ts, + {std::move(op_key)}, + [tmp_dir, // keep tmp_dir alive + key, + setter, + logger](auto const& values) { + GitOpValue op_result = *values[0]; + // check flag + if (not op_result.result) { + (*logger)("Keep tag failed", + /*fatal=*/true); + return; + } + JustMRProgress::Instance().TaskTracker().Stop( + key.origin); + // success + (*setter)(false /*no cache hit*/); + }, + [logger, + commit = *op_result.result, + target_path = tmp_dir->GetPath()](auto const& msg, + bool fatal) { + (*logger)( + fmt::format("While running critical Git op " + "KEEP_TAG for commit {} in " + "target {}:\n{}", + commit, + target_path.string(), + msg), + fatal); + }); + }, + [logger, target_path = tmp_dir->GetPath()](auto const& msg, + bool fatal) { + (*logger)( + fmt::format("While running critical Git op " + "INITIAL_COMMIT for target {}:\n{}", + target_path.string(), + msg), + fatal); + }); + }, + [logger, target_path = StorageConfig::GitRoot()](auto const& msg, + bool fatal) { + (*logger)(fmt::format("While running critical Git op " + "ENSURE_INIT bare for target {}:\n{}", + target_path.string(), + msg), + fatal); + }); + }; + return AsyncMapConsumer<GitTreeInfo, bool>(tree_to_cache, jobs); +} diff --git a/src/other_tools/ops_maps/git_tree_fetch_map.hpp b/src/other_tools/ops_maps/git_tree_fetch_map.hpp new file mode 100644 index 00000000..f84056a8 --- /dev/null +++ b/src/other_tools/ops_maps/git_tree_fetch_map.hpp @@ -0,0 +1,64 @@ +// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDED_SRC_OTHER_TOOLS_OPS_MAPS_GIT_TREE_FETCH_MAP_HPP +#define INCLUDED_SRC_OTHER_TOOLS_OPS_MAPS_GIT_TREE_FETCH_MAP_HPP + +#include <string> +#include <utility> +#include <vector> + +#include "nlohmann/json.hpp" +#include "src/buildtool/execution_api/common/execution_api.hpp" +#include "src/other_tools/ops_maps/critical_git_op_map.hpp" +#include "src/other_tools/ops_maps/import_to_git_map.hpp" + +// Stores all the information needed to make a Git tree available +struct GitTreeInfo { + std::string hash{}; /* key */ + std::map<std::string, std::string> env_vars{}; + std::vector<std::string> inherit_env{}; + std::vector<std::string> command{}; + // name of repository for which work is done; used in progress reporting + std::string origin{}; + + [[nodiscard]] auto operator==(const GitTreeInfo& other) const -> bool { + return hash == other.hash; + } +}; + +namespace std { +template <> +struct hash<GitTreeInfo> { + [[nodiscard]] auto operator()(const GitTreeInfo& gti) const noexcept + -> std::size_t { + return std::hash<std::string>{}(gti.hash); + } +}; +} // namespace std + +/// \brief Maps a known tree provided through a generic command to a flag +/// signaling if there was a cache hit (i.e., tree was already present). +using GitTreeFetchMap = AsyncMapConsumer<GitTreeInfo, bool>; + +[[nodiscard]] auto CreateGitTreeFetchMap( + gsl::not_null<CriticalGitOpMap*> const& critical_git_op_map, + gsl::not_null<ImportToGitMap*> const& import_to_git_map, + std::string const& git_bin, + std::vector<std::string> const& launcher, + IExecutionApi* local_api, + IExecutionApi* remote_api, + std::size_t jobs) -> GitTreeFetchMap; + +#endif // INCLUDED_SRC_OTHER_TOOLS_OPS_MAPS_GIT_TREE_FETCH_MAP_HPP diff --git a/src/other_tools/repo_map/TARGETS b/src/other_tools/repo_map/TARGETS index c397d82c..166e0696 100644 --- a/src/other_tools/repo_map/TARGETS +++ b/src/other_tools/repo_map/TARGETS @@ -17,6 +17,8 @@ , ["src/other_tools/just_mr/progress_reporting", "statistics"] , ["src/buildtool/file_system", "file_root"] , ["src/buildtool/file_system/symlinks_map", "pragma_special"] + , ["src/other_tools/ops_maps", "content_cas_map"] + , ["src/other_tools/ops_maps", "git_tree_fetch_map"] ] } } diff --git a/src/other_tools/repo_map/repos_to_setup_map.cpp b/src/other_tools/repo_map/repos_to_setup_map.cpp index c0a8cac1..3f9390b2 100644 --- a/src/other_tools/repo_map/repos_to_setup_map.cpp +++ b/src/other_tools/repo_map/repos_to_setup_map.cpp @@ -18,6 +18,8 @@ #include "src/buildtool/file_system/symlinks_map/pragma_special.hpp" #include "src/other_tools/just_mr/progress_reporting/progress.hpp" #include "src/other_tools/just_mr/progress_reporting/statistics.hpp" +#include "src/other_tools/ops_maps/content_cas_map.hpp" +#include "src/other_tools/ops_maps/git_tree_fetch_map.hpp" namespace { @@ -740,10 +742,10 @@ void GitTreeCheckout(ExpressionPtr const& repo_desc, pragma_absent->get()->Bool(); // populate struct TreeIdInfo tree_id_info = { - .hash = repo_desc_hash->get()->String(), - .env_vars = std::move(env), - .inherit_env = std::move(inherit_env), - .command = std::move(cmd), + .tree_info = GitTreeInfo{.hash = repo_desc_hash->get()->String(), + .env_vars = std::move(env), + .inherit_env = std::move(inherit_env), + .command = std::move(cmd)}, .ignore_special = pragma_special_value == PragmaSpecial::Ignore, .absent = not fetch_absent and pragma_absent_value}; // get the WS root as git tree diff --git a/src/other_tools/root_maps/TARGETS b/src/other_tools/root_maps/TARGETS index 4669d30c..6a5955e2 100644 --- a/src/other_tools/root_maps/TARGETS +++ b/src/other_tools/root_maps/TARGETS @@ -115,24 +115,12 @@ , "srcs": ["tree_id_git_map.cpp"] , "deps": [ ["@", "json", "", "json"] - , ["src/buildtool/execution_api/common", "common"] - , ["src/other_tools/ops_maps", "critical_git_op_map"] - , ["src/other_tools/ops_maps", "import_to_git_map"] + , ["src/other_tools/ops_maps", "git_tree_fetch_map"] , ["src/utils/cpp", "hash_combine"] ] , "stage": ["src", "other_tools", "root_maps"] , "private-deps": - [ ["src/other_tools/ops_maps", "critical_git_op_map"] - , ["src/buildtool/execution_api/common", "common"] - , ["src/buildtool/file_system", "file_system_manager"] - , ["src/buildtool/storage", "config"] - , ["src/buildtool/storage", "fs_utils"] - , ["src/buildtool/system", "system_command"] - , ["src/other_tools/git_operations", "git_repo_remote"] - , ["src/other_tools/just_mr/progress_reporting", "progress"] - , ["src/other_tools/just_mr/progress_reporting", "statistics"] - , ["src/other_tools/ops_maps", "content_cas_map"] - , ["src/other_tools/ops_maps", "import_to_git_map"] + [ ["src/buildtool/storage", "config"] , ["src/buildtool/file_system", "file_root"] , ["@", "fmt", "", "fmt"] ] diff --git a/src/other_tools/root_maps/tree_id_git_map.cpp b/src/other_tools/root_maps/tree_id_git_map.cpp index a61ded3a..faedccf4 100644 --- a/src/other_tools/root_maps/tree_id_git_map.cpp +++ b/src/other_tools/root_maps/tree_id_git_map.cpp @@ -14,457 +14,51 @@ #include "src/other_tools/root_maps/tree_id_git_map.hpp" -#include <cstdlib> - #include "fmt/core.h" -#include "src/buildtool/execution_api/common/execution_common.hpp" #include "src/buildtool/file_system/file_root.hpp" -#include "src/buildtool/file_system/file_system_manager.hpp" #include "src/buildtool/storage/config.hpp" -#include "src/buildtool/storage/fs_utils.hpp" -#include "src/buildtool/system/system_command.hpp" -#include "src/other_tools/git_operations/git_repo_remote.hpp" -#include "src/other_tools/just_mr/progress_reporting/progress.hpp" -#include "src/other_tools/just_mr/progress_reporting/statistics.hpp" - -namespace { - -void KeepCommitAndSetRoot( - TreeIdInfo const& tree_id_info, - std::string const& commit, - TmpDirPtr const& tmp_dir, // need to keep tmp_dir alive - gsl::not_null<CriticalGitOpMap*> const& critical_git_op_map, - gsl::not_null<TaskSystem*> const& ts, - TreeIdGitMap::SetterPtr const& ws_setter, - TreeIdGitMap::LoggerPtr const& logger) { - // Keep tag for commit - GitOpKey op_key = {.params = - { - StorageConfig::GitRoot(), // target_path - commit, // git_hash - "", // branch - "Keep referenced tree alive" // message - }, - .op_type = GitOpType::KEEP_TAG}; - critical_git_op_map->ConsumeAfterKeysReady( - ts, - {std::move(op_key)}, - [tmp_dir, // keep tmp_dir alive - tree_id_info, - ws_setter, - logger](auto const& values) { - GitOpValue op_result = *values[0]; - // check flag - if (not op_result.result) { - (*logger)("Keep tag failed", - /*fatal=*/true); - return; - } - JustMRProgress::Instance().TaskTracker().Stop(tree_id_info.origin); - // set the workspace root - auto root = nlohmann::json::array( - {tree_id_info.ignore_special - ? FileRoot::kGitTreeIgnoreSpecialMarker - : FileRoot::kGitTreeMarker, - tree_id_info.hash}); - if (not tree_id_info.absent) { - root.emplace_back(StorageConfig::GitRoot().string()); - } - (*ws_setter)(std::pair(std::move(root), false)); - }, - [logger, commit, target_path = tmp_dir->GetPath()](auto const& msg, - bool fatal) { - (*logger)(fmt::format("While running critical Git op KEEP_TAG for " - "commit {} in target {}:\n{}", - commit, - target_path.string(), - msg), - fatal); - }); -} - -} // namespace auto CreateTreeIdGitMap( - gsl::not_null<CriticalGitOpMap*> const& critical_git_op_map, - gsl::not_null<ImportToGitMap*> const& import_to_git_map, - std::string const& git_bin, - std::vector<std::string> const& launcher, - IExecutionApi* local_api, - IExecutionApi* remote_api, + gsl::not_null<GitTreeFetchMap*> const& git_tree_fetch_map, std::size_t jobs) -> TreeIdGitMap { - auto tree_to_git = [critical_git_op_map, - import_to_git_map, - git_bin, - launcher, - local_api, - remote_api](auto ts, - auto setter, - auto logger, - auto /*unused*/, - auto const& key) { + auto tree_to_git = [git_tree_fetch_map](auto ts, + auto setter, + auto logger, + auto /*unused*/, + auto const& key) { // if root is absent, no work needs to be done if (key.absent) { auto root = nlohmann::json::array( {key.ignore_special ? FileRoot::kGitTreeIgnoreSpecialMarker : FileRoot::kGitTreeMarker, - key.hash}); + key.tree_info.hash}); (*setter)(std::pair(std::move(root), false)); return; } - // check whether tree exists already in CAS - // ensure Git cache - // define Git operation to be done - GitOpKey op_key = {.params = - { - StorageConfig::GitRoot(), // target_path - "", // git_hash - "", // branch - std::nullopt, // message - true // init_bare - }, - .op_type = GitOpType::ENSURE_INIT}; - critical_git_op_map->ConsumeAfterKeysReady( + // make sure the required tree is in Git cache + git_tree_fetch_map->ConsumeAfterKeysReady( ts, - {std::move(op_key)}, - [critical_git_op_map, - import_to_git_map, - git_bin, - launcher, - local_api, - remote_api, - key, - ts, - setter, - logger](auto const& values) { - GitOpValue op_result = *values[0]; - // check flag - if (not op_result.result) { - (*logger)("Git cache init failed", - /*fatal=*/true); - return; - } - // Open fake tmp repo to check if tree is known to Git cache - auto git_repo = GitRepoRemote::Open( - op_result.git_cas); // link fake repo to odb - if (not git_repo) { - (*logger)(fmt::format("Could not open repository {}", - StorageConfig::GitRoot().string()), - /*fatal=*/true); - return; - } - // setup wrapped logger - auto wrapped_logger = std::make_shared<AsyncMapConsumerLogger>( - [logger](auto const& msg, bool fatal) { - (*logger)(fmt::format("While checking tree exists in " - "Git cache:\n{}", - msg), - fatal); - }); - // check if the desired tree ID is in Git cache - auto tree_found = - git_repo->CheckTreeExists(key.hash, wrapped_logger); - if (not tree_found) { - // errors encountered - return; - } - if (not *tree_found) { - JustMRProgress::Instance().TaskTracker().Start(key.origin); - // check if tree is in remote CAS, if a remote is given - auto digest = ArtifactDigest{key.hash, 0, /*is_tree=*/true}; - if (remote_api != nullptr and local_api != nullptr and - remote_api->IsAvailable(digest) and - remote_api->RetrieveToCas( - {Artifact::ObjectInfo{.digest = digest, - .type = ObjectType::Tree}}, - local_api)) { - JustMRProgress::Instance().TaskTracker().Stop( - key.origin); - // Move tree from CAS to local Git storage - auto tmp_dir = StorageUtils::CreateTypedTmpDir( - "fetch-remote-git-tree"); - if (not tmp_dir) { - (*logger)(fmt::format("Failed to create tmp " - "directory for copying " - "git-tree {} from remote CAS", - key.hash), - true); - return; - } - if (not local_api->RetrieveToPaths( - {Artifact::ObjectInfo{ - .digest = digest, - .type = ObjectType::Tree}}, - {tmp_dir->GetPath()})) { - (*logger)( - fmt::format("Failed to copy git-tree {} to {}", - key.hash, - tmp_dir->GetPath().string()), - true); - return; - } - CommitInfo c_info{tmp_dir->GetPath(), "tree", key.hash}; - import_to_git_map->ConsumeAfterKeysReady( - ts, - {std::move(c_info)}, - [tmp_dir, // keep tmp_dir alive - key, - setter, - logger](auto const& values) { - if (not values[0]->second) { - (*logger)("Importing to git failed", - /*fatal=*/true); - return; - } - // set the workspace root - auto root = nlohmann::json::array( - {key.ignore_special - ? FileRoot::kGitTreeIgnoreSpecialMarker - : FileRoot::kGitTreeMarker, - key.hash}); - if (not key.absent) { - root.emplace_back( - StorageConfig::GitRoot().string()); - } - (*setter)(std::pair(std::move(root), false)); - }, - [logger, tmp_dir, tree_id = key.hash]( - auto const& msg, bool fatal) { - (*logger)( - fmt::format("While moving git-tree {} from " - "{} to local git:\n{}", - tree_id, - tmp_dir->GetPath().string(), - msg), - fatal); - }); - - return; - } - // create temporary location for command execution root - auto tmp_dir = StorageUtils::CreateTypedTmpDir("git-tree"); - if (not tmp_dir) { - (*logger)( - "Failed to create tmp directory for tree id map!", - /*fatal=*/true); - return; - } - // create temporary location for storing command result - // files - auto out_dir = StorageUtils::CreateTypedTmpDir("git-tree"); - if (not out_dir) { - (*logger)( - "Failed to create tmp directory for tree id map!", - /*fatal=*/true); - return; - } - // execute command in temporary location - SystemCommand system{key.hash}; - auto cmdline = launcher; - std::copy(key.command.begin(), - key.command.end(), - std::back_inserter(cmdline)); - std::map<std::string, std::string> env{key.env_vars}; - for (auto const& k : key.inherit_env) { - const char* v = std::getenv(k.c_str()); - if (v != nullptr) { - env[k] = std::string(v); - } - } - auto const command_output = system.Execute( - cmdline, env, tmp_dir->GetPath(), out_dir->GetPath()); - if (not command_output) { - (*logger)(fmt::format("Failed to execute command:\n{}", - nlohmann::json(cmdline).dump()), - /*fatal=*/true); - return; - } - - // do an import to git with tree check - GitOpKey op_key = { - .params = - { - tmp_dir->GetPath(), // target_path - "", // git_hash - "", // branch - fmt::format("Content of tree {}", - key.hash), // message - }, - .op_type = GitOpType::INITIAL_COMMIT}; - critical_git_op_map->ConsumeAfterKeysReady( - ts, - {std::move(op_key)}, - [tmp_dir, // keep tmp_dir alive - out_dir, // keep stdout/stderr of command alive - critical_git_op_map, - just_git_cas = op_result.git_cas, - cmdline, - command_output, - key, - git_bin, - launcher, - ts, - setter, - logger](auto const& values) { - GitOpValue op_result = *values[0]; - // check flag - if (not op_result.result) { - (*logger)("Commit failed", - /*fatal=*/true); - return; - } - // Open fake tmp repository to check for tree - auto git_repo = GitRepoRemote::Open( - op_result.git_cas); // link fake repo to odb - if (not git_repo) { - (*logger)( - fmt::format("Could not open repository {}", - tmp_dir->GetPath().string()), - /*fatal=*/true); - return; - } - // setup wrapped logger - auto wrapped_logger = - std::make_shared<AsyncMapConsumerLogger>( - [logger](auto const& msg, bool fatal) { - (*logger)( - fmt::format("While checking tree " - "exists:\n{}", - msg), - fatal); - }); - // check that the desired tree ID is part of the - // repo - auto tree_check = git_repo->CheckTreeExists( - key.hash, wrapped_logger); - if (not tree_check) { - // errors encountered - return; - } - if (not *tree_check) { - std::string out_str{}; - std::string err_str{}; - auto cmd_out = FileSystemManager::ReadFile( - command_output->stdout_file); - auto cmd_err = FileSystemManager::ReadFile( - command_output->stderr_file); - if (cmd_out) { - out_str = *cmd_out; - } - if (cmd_err) { - err_str = *cmd_err; - } - std::string output{}; - if (!out_str.empty() || !err_str.empty()) { - output = fmt::format( - ".\nOutput of command:\n{}{}", - out_str, - err_str); - } - (*logger)( - fmt::format("Executing {} did not create " - "specified tree {}{}", - nlohmann::json(cmdline).dump(), - key.hash, - output), - /*fatal=*/true); - return; - } - auto target_path = tmp_dir->GetPath(); - // fetch all into Git cache - auto just_git_repo = - GitRepoRemote::Open(just_git_cas); - if (not just_git_repo) { - (*logger)(fmt::format("Could not open Git " - "repository {}", - target_path.string()), - /*fatal=*/true); - return; - } - // define temp repo path - auto tmp_dir = - StorageUtils::CreateTypedTmpDir("git-tree"); - ; - if (not tmp_dir) { - (*logger)(fmt::format("Could not create unique " - "path for target {}", - target_path.string()), - /*fatal=*/true); - return; - } - wrapped_logger = - std::make_shared<AsyncMapConsumerLogger>( - [logger, target_path](auto const& msg, - bool fatal) { - (*logger)( - fmt::format( - "While fetch via tmp repo " - "for target {}:\n{}", - target_path.string(), - msg), - fatal); - }); - if (not just_git_repo->FetchViaTmpRepo( - tmp_dir->GetPath(), - target_path.string(), - std::nullopt, - git_bin, - launcher, - wrapped_logger)) { - return; - } - // setup a wrapped_logger - wrapped_logger = - std::make_shared<AsyncMapConsumerLogger>( - [logger, target_path](auto const& msg, - bool fatal) { - (*logger)(fmt::format( - "While doing keep commit " - "and setting Git tree " - "for target {}:\n{}", - target_path.string(), - msg), - fatal); - }); - KeepCommitAndSetRoot( - key, - *op_result.result, // commit id - tmp_dir, - critical_git_op_map, - ts, - setter, - wrapped_logger); - }, - [logger, target_path = tmp_dir->GetPath()]( - auto const& msg, bool fatal) { - (*logger)( - fmt::format("While running critical Git op " - "INITIAL_COMMIT for target {}:\n{}", - target_path.string(), - msg), - fatal); - }); - } - else { - // tree found, so return the git tree root as-is - auto root = nlohmann::json::array( - {key.ignore_special - ? FileRoot::kGitTreeIgnoreSpecialMarker - : FileRoot::kGitTreeMarker, - key.hash}); - if (not key.absent) { - root.emplace_back(StorageConfig::GitRoot().string()); - } - (*setter)(std::pair(std::move(root), true)); + {key.tree_info}, + [key, setter](auto const& values) { + // tree is now in Git cache; + // get cache hit info + auto is_cache_hit = *values[0]; + // set the workspace root + auto root = nlohmann::json::array( + {key.ignore_special ? FileRoot::kGitTreeIgnoreSpecialMarker + : FileRoot::kGitTreeMarker, + key.tree_info.hash}); + if (not key.absent) { + root.emplace_back(StorageConfig::GitRoot().string()); } + (*setter)(std::pair(std::move(root), is_cache_hit)); }, - [logger, target_path = StorageConfig::GitRoot()](auto const& msg, - bool fatal) { - (*logger)(fmt::format("While running critical Git " - "op ENSURE_INIT bare for " - "target {}:\n{}", - target_path.string(), - msg), + [logger, tree_id = key.tree_info.hash](auto const& msg, + bool fatal) { + (*logger)(fmt::format( + "While ensuring git-tree {} is in Git cache:\n{}", + tree_id, + msg), fatal); }); }; diff --git a/src/other_tools/root_maps/tree_id_git_map.hpp b/src/other_tools/root_maps/tree_id_git_map.hpp index 04708e3a..89d1af08 100644 --- a/src/other_tools/root_maps/tree_id_git_map.hpp +++ b/src/other_tools/root_maps/tree_id_git_map.hpp @@ -19,26 +19,19 @@ #include <utility> #include <vector> -#include "nlohmann/json.hpp" -#include "src/buildtool/execution_api/common/execution_api.hpp" -#include "src/other_tools/ops_maps/critical_git_op_map.hpp" -#include "src/other_tools/ops_maps/import_to_git_map.hpp" +#include "src/other_tools/ops_maps/git_tree_fetch_map.hpp" #include "src/utils/cpp/hash_combine.hpp" struct TreeIdInfo { - std::string hash{}; /* key */ - std::map<std::string, std::string> env_vars{}; - std::vector<std::string> inherit_env{}; - std::vector<std::string> command{}; - // name of repository for which work is done; used in progress reporting - std::string origin{}; + GitTreeInfo tree_info{}; /* key */ // create root that ignores symlinks bool ignore_special{}; /* key */ // create an absent root bool absent{}; /* key */ [[nodiscard]] auto operator==(const TreeIdInfo& other) const -> bool { - return hash == other.hash and ignore_special == other.ignore_special and + return tree_info == other.tree_info and + ignore_special == other.ignore_special and absent == other.absent; } }; @@ -49,7 +42,7 @@ struct hash<TreeIdInfo> { [[nodiscard]] auto operator()(const TreeIdInfo& ti) const noexcept -> std::size_t { size_t seed{}; - hash_combine<std::string>(&seed, ti.hash); + hash_combine<GitTreeInfo>(&seed, ti.tree_info); hash_combine<bool>(&seed, ti.ignore_special); hash_combine<bool>(&seed, ti.absent); return seed; @@ -58,17 +51,12 @@ struct hash<TreeIdInfo> { } // namespace std /// \brief Maps a known tree provided through a generic command to its -/// workspace root and the information whether it was a cache it. +/// workspace root and the information whether it was a cache hit. using TreeIdGitMap = AsyncMapConsumer<TreeIdInfo, std::pair<nlohmann::json, bool>>; [[nodiscard]] auto CreateTreeIdGitMap( - gsl::not_null<CriticalGitOpMap*> const& critical_git_op_map, - gsl::not_null<ImportToGitMap*> const& import_to_git_map, - std::string const& git_bin, - std::vector<std::string> const& launcher, - IExecutionApi* local_api, - IExecutionApi* remote_api, + gsl::not_null<GitTreeFetchMap*> const& git_tree_fetch_map, std::size_t jobs) -> TreeIdGitMap; #endif // INCLUDED_SRC_OTHER_TOOLS_ROOT_MAPS_TREE_ID_GIT_MAP_HPP |