diff options
Diffstat (limited to 'src/buildtool/serve_api/serve_service/source_tree.cpp')
-rw-r--r-- | src/buildtool/serve_api/serve_service/source_tree.cpp | 145 |
1 files changed, 145 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 new file mode 100644 index 00000000..8d91c2f5 --- /dev/null +++ b/src/buildtool/serve_api/serve_service/source_tree.cpp @@ -0,0 +1,145 @@ +// 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/buildtool/serve_api/serve_service/source_tree.hpp" + +#include "fmt/core.h" +#include "src/buildtool/common/artifact.hpp" +#include "src/buildtool/common/artifact_digest.hpp" +#include "src/buildtool/execution_api/bazel_msg/bazel_common.hpp" +#include "src/buildtool/execution_api/local/local_api.hpp" +#include "src/buildtool/execution_api/remote/bazel/bazel_api.hpp" +#include "src/buildtool/file_system/git_repo.hpp" +#include "src/buildtool/serve_api/remote/config.hpp" +#include "src/buildtool/storage/config.hpp" + +auto SourceTreeService::CreateExecutionApi( + std::optional<RemoteExecutionConfig::ServerAddress> const& address) + -> gsl::not_null<IExecutionApi::Ptr> { + if (address) { + ExecutionConfiguration config; + config.skip_cache_lookup = false; + + return std::make_unique<BazelApi>( + "remote-execution", address->host, address->port, config); + } + return std::make_unique<LocalApi>(); +} + +auto SourceTreeService::GetTreeFromCommit( + std::filesystem::path const& repo_path, + std::string const& commit, + std::string const& subdir, + std::shared_ptr<Logger> const& logger) -> std::optional<std::string> { + 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, commit](auto const& msg, bool fatal) { + if (fatal) { + auto err = fmt::format( + "ServeCommitTree: While retrieving tree of commit " + "{} from repository {}:\n{}", + commit, + repo_path.string(), + msg); + logger->Emit(LogLevel::Trace, err); + } + }); + if (auto tree_id = repo->GetSubtreeFromCommit( + commit, subdir, wrapped_logger)) { + return tree_id; + } + } + } + return std::nullopt; +} + +auto SourceTreeService::ServeCommitTree( + ::grpc::ServerContext* /* context */, + const ::justbuild::just_serve::ServeCommitTreeRequest* request, + ::justbuild::just_serve::ServeCommitTreeResponse* response) + -> ::grpc::Status { + using ServeCommitTreeResponse = + ::justbuild::just_serve::ServeCommitTreeResponse; + auto const& commit{request->commit()}; + auto const& subdir{request->subdir()}; + // try in local build root Git cache + if (auto tree_id = GetTreeFromCommit( + StorageConfig::GitRoot(), commit, subdir, logger_)) { + if (request->sync_tree()) { + // sync tree with 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::Debug, str); + response->set_status(ServeCommitTreeResponse::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::Debug, str); + *(response->mutable_tree()) = std::move(*tree_id); + response->set_status(ServeCommitTreeResponse::SYNC_ERROR); + return ::grpc::Status::OK; + } + } + // set response + *(response->mutable_tree()) = std::move(*tree_id); + response->set_status(ServeCommitTreeResponse::OK); + return ::grpc::Status::OK; + } + // try given extra repositories, in order + for (auto const& path : RemoteServeConfig::KnownRepositories()) { + if (auto tree_id = GetTreeFromCommit(path, commit, subdir, logger_)) { + + if (request->sync_tree()) { + // sync tree with 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::Debug, str); + response->set_status( + ServeCommitTreeResponse::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::Debug, str); + *(response->mutable_tree()) = std::move(*tree_id); + response->set_status(ServeCommitTreeResponse::SYNC_ERROR); + } + } + // set response + *(response->mutable_tree()) = std::move(*tree_id); + response->set_status(ServeCommitTreeResponse::OK); + return ::grpc::Status::OK; + } + } + // commit not found + response->set_status(ServeCommitTreeResponse::NOT_FOUND); + return ::grpc::Status::OK; +} |