summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/buildtool/execution_engine/executor/executor.hpp143
1 files changed, 126 insertions, 17 deletions
diff --git a/src/buildtool/execution_engine/executor/executor.hpp b/src/buildtool/execution_engine/executor/executor.hpp
index 721d4896..10f578cf 100644
--- a/src/buildtool/execution_engine/executor/executor.hpp
+++ b/src/buildtool/execution_engine/executor/executor.hpp
@@ -116,10 +116,10 @@ class ExecutorImpl {
// is available to the execution API
if (object_info_opt) {
if (not api->IsAvailable(object_info_opt->digest) and
- not UploadKnownArtifact(api,
- artifact->Content().Repository(),
- object_info_opt->digest,
- /*skip_check=*/true)) {
+ not VerifyOrUploadKnownArtifact(
+ api,
+ artifact->Content().Repository(),
+ object_info_opt->digest)) {
Logger::Log(
LogLevel::Error,
"artifact {} should be present in CAS but is missing.",
@@ -148,19 +148,116 @@ class ExecutorImpl {
return true;
}
+ /// \brief Uploads the content of a git tree recursively to the CAS. It is
+ /// first checked which elements of a directory are not available in the
+ /// CAS and the missing elements are uploaded accordingly. This ensures the
+ /// invariant that if a git tree is known to the CAS all its content is also
+ /// existing in the CAS.
+ /// \param[in] api The remote execution API of the CAS.
+ /// \param[in] tree The git tree to be uploaded.
+ /// \returns True if the upload was successful, False in case of any error.
+ // NOLINTNEXTLINE(misc-no-recursion)
+ [[nodiscard]] static auto VerifyOrUploadTree(
+ gsl::not_null<IExecutionApi*> const& api,
+ gsl::not_null<GitTreePtr> const& tree) noexcept -> bool {
+
+ // create list of digests for batch check of CAS availability
+ std::vector<ArtifactDigest> digests;
+ std::unordered_map<ArtifactDigest, gsl::not_null<GitTreeEntryPtr>>
+ entry_map;
+ for (auto const& [path, entry] : *tree) {
+ auto digest =
+ ArtifactDigest{entry->Hash(), *entry->Size(), entry->IsTree()};
+ digests.emplace_back(digest);
+ try {
+ entry_map.emplace(std::move(digest), entry);
+ } catch (...) {
+ return false;
+ }
+ }
+
+ // find missing digests
+ auto missing_digests = api->IsAvailable(digests);
+
+ // process missing trees
+ for (auto const& digest : missing_digests) {
+ if (auto it = entry_map.find(digest); it != entry_map.end()) {
+ auto const& entry = it->second;
+ if (entry->IsTree()) {
+ if (not VerifyOrUploadTree(
+ api,
+ std::make_shared<GitTree const>(*entry->Tree()))) {
+ return false;
+ }
+ }
+ }
+ }
+
+ // upload missing entries (blobs or trees)
+ BlobContainer container;
+ for (auto const& digest : missing_digests) {
+ if (auto it = entry_map.find(digest); it != entry_map.end()) {
+ auto const& entry = it->second;
+ auto content = entry->RawData();
+ if (not content) {
+ return false;
+ }
+ try {
+ container.Emplace(
+ std::move(BazelBlob{digest, std::move(*content)}));
+ } catch (std::exception const& ex) {
+ Logger::Log(LogLevel::Error,
+ "failed to create blob with: ",
+ ex.what());
+ return false;
+ }
+ }
+ }
+
+ return api->Upload(container, /*skip_find_missing=*/true);
+ }
+
/// \brief Lookup blob via digest in local git repositories and upload.
/// \param api The endpoint used for uploading
/// \param repo The global repository name, the artifact belongs to
/// \param digest The digest of the object
- /// \param skip_check Skip check for existence before upload
/// \param hash The git-sha1 hash of the object
/// \returns true on success
- [[nodiscard]] static auto UploadGitBlob(
+ [[nodiscard]] static auto VerifyOrUploadGitArtifact(
gsl::not_null<IExecutionApi*> const& api,
std::string const& repo,
ArtifactDigest const& digest,
- bool skip_check,
std::string const& hash) noexcept -> bool {
+ std::optional<std::string> content;
+ if (digest.is_tree()) {
+ // if known tree is not available, recursively upload its content
+ auto tree = ReadGitTree(repo, hash);
+ if (not tree) {
+ return false;
+ }
+ if (not VerifyOrUploadTree(
+ api, std::make_shared<GitTree const>(*tree))) {
+ return false;
+ }
+ content = tree->RawData();
+ }
+ else {
+ // if known blob is not available, read and upload it
+ content = ReadGitBlob(repo, hash);
+ }
+ if (not content) {
+ return false;
+ }
+
+ // upload artifact content
+ auto container =
+ BlobContainer{{BazelBlob{digest, std::move(*content)}}};
+ return api->Upload(container, /*skip_find_missing=*/true);
+ }
+
+ [[nodiscard]] static auto ReadGitBlob(std::string const& repo,
+ std::string const& hash) noexcept
+ -> std::optional<std::string> {
auto const& repo_config = RepositoryConfig::Instance();
std::optional<std::string> blob{};
if (auto const* ws_root = repo_config.WorkspaceRoot(repo)) {
@@ -171,32 +268,44 @@ class ExecutorImpl {
// try to obtain blob from global Git CAS, if any
blob = repo_config.ReadBlobFromGitCAS(hash);
}
- return blob and
- api->Upload(BlobContainer{{BazelBlob{digest, std::move(*blob)}}},
- skip_check);
+ return blob;
+ }
+
+ [[nodiscard]] static auto ReadGitTree(std::string const& repo,
+ std::string const& hash) noexcept
+ -> std::optional<GitTree> {
+ auto const& repo_config = RepositoryConfig::Instance();
+ std::optional<GitTree> tree{};
+ if (auto const* ws_root = repo_config.WorkspaceRoot(repo)) {
+ // try to obtain tree from local workspace's Git CAS, if any
+ tree = ws_root->ReadTree(hash);
+ }
+ if (not tree) {
+ // try to obtain tree from global Git CAS, if any
+ tree = repo_config.ReadTreeFromGitCAS(hash);
+ }
+ return tree;
}
/// \brief Lookup blob via digest in local git repositories and upload.
/// \param api The endpoint used for uploading
/// \param repo The global repository name, the artifact belongs to
/// \param digest The digest of the object
- /// \param skip_check Skip check for existence before upload
/// \returns true on success
- [[nodiscard]] static auto UploadKnownArtifact(
+ [[nodiscard]] static auto VerifyOrUploadKnownArtifact(
gsl::not_null<IExecutionApi*> const& api,
std::string const& repo,
- ArtifactDigest const& digest,
- bool skip_check) noexcept -> bool {
+ ArtifactDigest const& digest) noexcept -> bool {
if (Compatibility::IsCompatible()) {
auto opt = Compatibility::GetGitEntry(digest.hash());
if (opt) {
auto const& [git_sha1_hash, comp_repo] = *opt;
- return UploadGitBlob(
- api, comp_repo, digest, skip_check, git_sha1_hash);
+ return VerifyOrUploadGitArtifact(
+ api, comp_repo, digest, git_sha1_hash);
}
return false;
}
- return UploadGitBlob(api, repo, digest, skip_check, digest.hash());
+ return VerifyOrUploadGitArtifact(api, repo, digest, digest.hash());
}
/// \brief Lookup file via path in local workspace root and upload.