diff options
-rw-r--r-- | src/buildtool/serve_api/serve_service/source_tree.cpp | 103 | ||||
-rw-r--r-- | src/buildtool/serve_api/serve_service/source_tree.hpp | 14 |
2 files changed, 117 insertions, 0 deletions
diff --git a/src/buildtool/serve_api/serve_service/source_tree.cpp b/src/buildtool/serve_api/serve_service/source_tree.cpp index 3b7c81d6..739abc36 100644 --- a/src/buildtool/serve_api/serve_service/source_tree.cpp +++ b/src/buildtool/serve_api/serve_service/source_tree.cpp @@ -92,6 +92,8 @@ auto SymlinksResolveToPragmaSpecial( } // namespace +// Helper methods + auto SourceTreeService::GetSubtreeFromCommit( std::filesystem::path const& repo_path, std::string const& commit, @@ -558,6 +560,35 @@ auto SourceTreeService::ImportToGit( response); } +auto SourceTreeService::IsTreeInRepo(std::string const& tree_id, + std::filesystem::path const& repo_path, + std::shared_ptr<Logger> const& logger) + -> bool { + if (auto git_cas = GitCAS::Open(repo_path)) { + if (auto repo = GitRepo::Open(git_cas)) { + // wrap logger for GitRepo call + auto wrapped_logger = std::make_shared<GitRepo::anon_logger_t>( + [logger, repo_path, tree_id](auto const& msg, bool fatal) { + if (fatal) { + auto err = fmt::format( + "SourceTreeService: While checking existence of " + "tree {} in repository {}:\n{}", + tree_id, + repo_path.string(), + msg); + logger->Emit(LogLevel::Info, err); + } + }); + if (auto res = repo->CheckTreeExists(tree_id, wrapped_logger)) { + return *res; + } + } + } + return false; // tree not found +} + +// RPC implementations + auto SourceTreeService::ServeArchiveTree( ::grpc::ServerContext* /* context */, const ::justbuild::just_serve::ServeArchiveTreeRequest* request, @@ -757,3 +788,75 @@ auto SourceTreeService::ServeContent( response->set_status(ServeContentResponse::NOT_FOUND); return ::grpc::Status::OK; } + +auto SourceTreeService::ServeTree( + ::grpc::ServerContext* /* context */, + const ::justbuild::just_serve::ServeTreeRequest* request, + ServeTreeResponse* response) -> ::grpc::Status { + auto const& tree_id{request->tree()}; + // acquire lock for CAS + auto lock = GarbageCollector::SharedLock(); + if (!lock) { + auto str = fmt::format("Could not acquire gc SharedLock"); + logger_->Emit(LogLevel::Error, str); + response->set_status(ServeTreeResponse::INTERNAL_ERROR); + return ::grpc::Status::OK; + } + // check if tree is in Git cache + if (IsTreeInRepo(tree_id, StorageConfig::GitRoot(), logger_)) { + // upload tree to remote CAS + auto digest = ArtifactDigest{tree_id, 0, /*is_tree=*/true}; + auto repo = RepositoryConfig{}; + if (not repo.SetGitCAS(StorageConfig::GitRoot())) { + auto str = fmt::format("Failed to SetGitCAS at {}", + StorageConfig::GitRoot().string()); + logger_->Emit(LogLevel::Error, str); + response->set_status(ServeTreeResponse::INTERNAL_ERROR); + return ::grpc::Status::OK; + } + auto git_api = GitApi{&repo}; + if (not git_api.RetrieveToCas( + {Artifact::ObjectInfo{.digest = digest, + .type = ObjectType::Tree}}, + &(*remote_api_))) { + auto str = fmt::format("Failed to sync tree {}", tree_id); + logger_->Emit(LogLevel::Error, str); + response->set_status(ServeTreeResponse::SYNC_ERROR); + return ::grpc::Status::OK; + } + // success! + response->set_status(ServeTreeResponse::OK); + return ::grpc::Status::OK; + } + // check if tree is in a known repository + for (auto const& path : RemoteServeConfig::KnownRepositories()) { + if (IsTreeInRepo(tree_id, path, logger_)) { + // upload tree to remote CAS + auto digest = ArtifactDigest{tree_id, 0, /*is_tree=*/true}; + auto repo = RepositoryConfig{}; + if (not repo.SetGitCAS(path)) { + auto str = + fmt::format("Failed to SetGitCAS at {}", path.string()); + logger_->Emit(LogLevel::Error, str); + response->set_status(ServeTreeResponse::INTERNAL_ERROR); + return ::grpc::Status::OK; + } + auto git_api = GitApi{&repo}; + if (not git_api.RetrieveToCas( + {Artifact::ObjectInfo{.digest = digest, + .type = ObjectType::Tree}}, + &(*remote_api_))) { + auto str = fmt::format("Failed to sync tree {}", tree_id); + logger_->Emit(LogLevel::Error, str); + response->set_status(ServeTreeResponse::SYNC_ERROR); + return ::grpc::Status::OK; + } + // success! + response->set_status(ServeTreeResponse::OK); + return ::grpc::Status::OK; + } + } + // tree not known + response->set_status(ServeTreeResponse::NOT_FOUND); + return ::grpc::Status::OK; +} diff --git a/src/buildtool/serve_api/serve_service/source_tree.hpp b/src/buildtool/serve_api/serve_service/source_tree.hpp index 03687df4..1204d9b7 100644 --- a/src/buildtool/serve_api/serve_service/source_tree.hpp +++ b/src/buildtool/serve_api/serve_service/source_tree.hpp @@ -42,6 +42,7 @@ class SourceTreeService final ::justbuild::just_serve::ServeArchiveTreeResponse; using ServeContentResponse = ::justbuild::just_serve::ServeContentResponse; + using ServeTreeResponse = ::justbuild::just_serve::ServeTreeResponse; // Retrieve the Git-subtree identifier from a given Git commit. // @@ -69,6 +70,14 @@ class SourceTreeService final const ::justbuild::just_serve::ServeContentRequest* request, ServeContentResponse* response) -> ::grpc::Status override; + // Make a given tree identifier available in remote CAS, + // if tree is known. + // + // There are no method-specific errors. + auto ServeTree(::grpc::ServerContext* context, + const ::justbuild::just_serve::ServeTreeRequest* request, + ServeTreeResponse* response) -> ::grpc::Status override; + private: mutable std::shared_mutex mutex_; std::shared_ptr<Logger> logger_{std::make_shared<Logger>("serve-service")}; @@ -123,6 +132,11 @@ class SourceTreeService final std::optional<PragmaSpecial> const& resolve_special, bool sync_tree, ServeArchiveTreeResponse* response) -> ::grpc::Status; + + [[nodiscard]] static auto IsTreeInRepo( + std::string const& tree_id, + std::filesystem::path const& repo_path, + std::shared_ptr<Logger> const& logger) -> bool; }; #endif // INCLUDED_SRC_BUILDTOOL_SERVE_API_SERVE_SERVICE_SOURCE_TREE_HPP |