diff options
author | Alberto Sartori <alberto.sartori@huawei.com> | 2023-01-23 18:31:14 +0100 |
---|---|---|
committer | Alberto Sartori <alberto.sartori@huawei.com> | 2023-02-02 17:57:19 +0100 |
commit | bd66d45945dc186a0d08db7d9845ef657d549577 (patch) | |
tree | 51fa0b9c630ed388fc8aa36f5314b30fdc6bd5ff /src/buildtool/execution_api/execution_service/execution_server.cpp | |
parent | 0658ef369e9dc27ca3a16075fc0f9e20931a2350 (diff) | |
download | justbuild-bd66d45945dc186a0d08db7d9845ef657d549577.tar.gz |
execution-service: add new subcommand execute
This subcommand starts a single node remote execution service honoring
the just native remote protocol.
If the flag --compatible is provided, the execution service will honor
the original remote build execution protocol.
New command line args supported by this subcommand:
-p,--port INT: Execution service will listen to this port. If unset,
the service will listen to the first available one.
--info-file TEXT: Write the used port, interface, and pid to this file
in JSON format. If the file exists, it will be overwritten.
-i,--interface TEXT: Interface to use. If unset, the loopback device
is used.
--pid-file TEXT Write pid to this file in plain txt. If the file
exists, it will be overwritten.
--tls-server-cert TEXT: Path to the TLS server certificate.
--tls-server-key TEXT: Path to the TLS server key.
Co-authored by: Klaus Aehlig <klaus.aehlig@huawei.com>
Diffstat (limited to 'src/buildtool/execution_api/execution_service/execution_server.cpp')
-rw-r--r-- | src/buildtool/execution_api/execution_service/execution_server.cpp | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/src/buildtool/execution_api/execution_service/execution_server.cpp b/src/buildtool/execution_api/execution_service/execution_server.cpp new file mode 100644 index 00000000..774128a2 --- /dev/null +++ b/src/buildtool/execution_api/execution_service/execution_server.cpp @@ -0,0 +1,153 @@ +// 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/execution_api/execution_service/execution_server.hpp" + +#include <algorithm> +#include <fstream> +#include <iostream> +#include <string> + +#include "fmt/format.h" +#include "src/buildtool/execution_api/local/garbage_collector.hpp" + +auto ExecutionServiceImpl::Execute( + ::grpc::ServerContext* /*context*/, + const ::build::bazel::remote::execution::v2::ExecuteRequest* request, + ::grpc::ServerWriter<::google::longrunning::Operation>* writer) + -> ::grpc::Status { + auto lock = GarbageCollector::SharedLock(); + if (!lock) { + auto str = fmt::format("Could not acquire SharedLock"); + logger_.Emit(LogLevel::Error, str); + return grpc::Status{grpc::StatusCode::INTERNAL, str}; + } + auto path = storage_.BlobPath(request->action_digest(), false); + if (!path) { + return ::grpc::Status{grpc::StatusCode::INTERNAL, + fmt::format("could not retrieve blob {} from cas", + request->action_digest().hash())}; + } + ::build::bazel::remote::execution::v2::Action a{}; + { + std::ifstream f(*path); + if (!a.ParseFromIstream(&f)) { + return ::grpc::Status{ + grpc::StatusCode::INTERNAL, + fmt::format("failed to parse action from blob {}", + request->action_digest().hash())}; + } + } + path = storage_.BlobPath(a.command_digest(), false); + if (!path) { + return ::grpc::Status{grpc::StatusCode::INTERNAL, + fmt::format("could not retrieve blob {} from cas", + request->action_digest().hash())}; + } + ::build::bazel::remote::execution::v2::Command c{}; + { + std::ifstream f(*path); + if (!c.ParseFromIstream(&f)) { + return ::grpc::Status{ + grpc::StatusCode::INTERNAL, + fmt::format("failed to parse command from blob {}", + a.command_digest().hash())}; + } + } + if (Compatibility::IsCompatible()) { + path = storage_.BlobPath(a.input_root_digest(), false); + } + else { + path = storage_.TreePath(a.input_root_digest()); + } + if (!path) { + return ::grpc::Status{grpc::StatusCode::INTERNAL, + fmt::format("could not retrieve tree {} from cas", + a.input_root_digest().hash())}; + } + auto op = ::google::longrunning::Operation{}; + op.set_name("just-remote-execution"); + std::map<std::string, std::string> env_vars; + for (auto const& x : c.environment_variables()) { + env_vars.emplace(x.name(), x.value()); + } + auto action = api_->CreateAction( + ArtifactDigest{a.input_root_digest()}, + {c.arguments().begin(), c.arguments().end()}, + {c.output_files().begin(), c.output_files().end()}, + {c.output_directories().begin(), c.output_directories().end()}, + env_vars, + {}); + logger_.Emit(LogLevel::Info, "Execute {}", request->action_digest().hash()); + auto tmp = action->Execute(&logger_); + ::build::bazel::remote::execution::v2::ExecuteResponse response{}; + for (auto const& [path, info] : tmp->Artifacts()) { + if (info.type == ObjectType::Tree) { + auto* d = response.mutable_result()->add_output_directories(); + *(d->mutable_path()) = path; + *(d->mutable_tree_digest()) = + static_cast<::build::bazel::remote::execution::v2::Digest>( + info.digest); + } + else { + auto* f = response.mutable_result()->add_output_files(); + *(f->mutable_path()) = path; + *(f->mutable_digest()) = + static_cast<::build::bazel::remote::execution::v2::Digest>( + info.digest); + if (info.type == ObjectType::Executable) { + f->set_is_executable(true); + } + } + } + response.set_cached_result(tmp->IsCached()); + + if (tmp->HasStdErr()) { + logger_.Emit(LogLevel::Error, tmp->StdErr()); + } + op.set_done(true); + ::google::rpc::Status status{}; + *(response.mutable_status()) = status; + response.mutable_result()->set_exit_code(tmp->ExitCode()); + if (tmp->HasStdErr()) { + response.mutable_result()->set_stderr_raw(tmp->StdErr().data()); + } + if (tmp->HasStdOut()) { + response.mutable_result()->set_stdout_raw(tmp->StdOut().data()); + } + + op.mutable_response()->PackFrom(response); + writer->Write(op); + if (tmp->ExitCode() == 0 && + !storage_.StoreActionResult(request->action_digest(), + response.result())) { + auto str = fmt::format("Could not store action result for action {}", + request->action_digest().hash()); + logger_.Emit(LogLevel::Error, str); + return ::grpc::Status{grpc::StatusCode::INTERNAL, str}; + } + + return ::grpc::Status::OK; +} + +auto ExecutionServiceImpl::WaitExecution( + ::grpc::ServerContext* /*context*/, + const ::build::bazel::remote::execution::v2:: + WaitExecutionRequest* /*request*/, + ::grpc::ServerWriter<::google::longrunning::Operation>* /*writer*/) + -> ::grpc::Status { + auto const* str = "WaitExecution not implemented"; + logger_.Emit(LogLevel::Error, str); + return ::grpc::Status{grpc::StatusCode::UNIMPLEMENTED, str}; +} |