summaryrefslogtreecommitdiff
path: root/src/buildtool/execution_api/execution_service/execution_server.hpp
blob: 09c750ec0c8679ce2300dfe83a6d35b71a9734d2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// 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 EXECUTION_SERVER_HPP
#define EXECUTION_SERVER_HPP

#include <cstdint>
#include <optional>
#include <string>

#include <grpcpp/grpcpp.h>

#include "build/bazel/remote/execution/v2/remote_execution.grpc.pb.h"
#include "google/longrunning/operations.pb.h"
#include "gsl/gsl"
#include "src/buildtool/common/bazel_types.hpp"
#include "src/buildtool/execution_api/common/execution_action.hpp"
#include "src/buildtool/execution_api/execution_service/operation_cache.hpp"
#include "src/buildtool/execution_api/local/context.hpp"
#include "src/buildtool/execution_api/local/local_api.hpp"
#include "src/buildtool/logging/logger.hpp"
#include "src/buildtool/storage/config.hpp"
#include "src/buildtool/storage/storage.hpp"
#include "src/utils/cpp/expected.hpp"

class LocalResponse;

class ExecutionServiceImpl final : public bazel_re::Execution::Service {
  public:
    explicit ExecutionServiceImpl(
        gsl::not_null<LocalContext const*> const& local_context,
        gsl::not_null<LocalApi const*> const& local_api,
        std::optional<std::uint8_t> op_exponent) noexcept
        : storage_config_{*local_context->storage_config},
          storage_{*local_context->storage},
          api_{*local_api} {
        if (op_exponent) {
            op_cache_.SetExponent(*op_exponent);
        }
    }

    [[nodiscard]] auto GetOpCache() const noexcept -> OperationCache const& {
        return op_cache_;
    }

    // Execute an action remotely.
    //
    // In order to execute an action, the client must first upload all of the
    // inputs, the
    // [Command][build.bazel.remote.execution.v2.Command] to run, and the
    // [Action][build.bazel.remote.execution.v2.Action] into the
    // [ContentAddressableStorage][build.bazel.remote.execution.v2.ContentAddressableStorage].
    // It then calls `Execute` with an `action_digest` referring to them. The
    // server will run the action and eventually return the result.
    //
    // The input `Action`'s fields MUST meet the various canonicalization
    // requirements specified in the documentation for their types so that it
    // has the same digest as other logically equivalent `Action`s. The server
    // MAY enforce the requirements and return errors if a non-canonical input
    // is received. It MAY also proceed without verifying some or all of the
    // requirements, such as for performance reasons. If the server does not
    // verify the requirement, then it will treat the `Action` as distinct from
    // another logically equivalent action if they hash differently.
    //
    // Returns a stream of
    // [google.longrunning.Operation][google.longrunning.Operation] messages
    // describing the resulting execution, with eventual `response`
    // [ExecuteResponse][build.bazel.remote.execution.v2.ExecuteResponse]. The
    // `metadata` on the operation is of type
    // [ExecuteOperationMetadata][build.bazel.remote.execution.v2.ExecuteOperationMetadata].
    //
    // If the client remains connected after the first response is returned
    // after the server, then updates are streamed as if the client had called
    // [WaitExecution][build.bazel.remote.execution.v2.Execution.WaitExecution]
    // until the execution completes or the request reaches an error. The
    // operation can also be queried using [Operations
    // API][google.longrunning.Operations.GetOperation].
    //
    // The server NEED NOT implement other methods or functionality of the
    // Operations API.
    //
    // Errors discovered during creation of the `Operation` will be reported
    // as gRPC Status errors, while errors that occurred while running the
    // action will be reported in the `status` field of the `ExecuteResponse`.
    // The server MUST NOT set the `error` field of the `Operation` proto. The
    // possible errors include:
    //
    // * `INVALID_ARGUMENT`: One or more arguments are invalid.
    // * `FAILED_PRECONDITION`: One or more errors occurred in setting up the
    //   action requested, such as a missing input or command or no worker being
    //   available. The client may be able to fix the errors and retry.
    // * `RESOURCE_EXHAUSTED`: There is insufficient quota of some resource to
    // run
    //   the action.
    // * `UNAVAILABLE`: Due to a transient condition, such as all workers being
    //   occupied (and the server does not support a queue), the action could
    //   not be started. The client should retry.
    // * `INTERNAL`: An internal error occurred in the execution engine or the
    //   worker.
    // * `DEADLINE_EXCEEDED`: The execution timed out.
    // * `CANCELLED`: The operation was cancelled by the client. This status is
    //   only possible if the server implements the Operations API
    //   CancelOperation method, and it was called for the current execution.
    //
    // In the case of a missing input or command, the server SHOULD additionally
    // send a [PreconditionFailure][google.rpc.PreconditionFailure] error detail
    // where, for each requested blob not present in the CAS, there is a
    // `Violation` with a `type` of `MISSING` and a `subject` of
    // `"blobs/{hash}/{size}"` indicating the digest of the missing blob.
    auto Execute(::grpc::ServerContext* context,
                 const ::bazel_re::ExecuteRequest* request,
                 ::grpc::ServerWriter<::google::longrunning::Operation>* writer)
        -> ::grpc::Status override;

    // Wait for an execution operation to complete. When the client initially
    // makes the request, the server immediately responds with the current
    // status of the execution. The server will leave the request stream open
    // until the operation completes, and then respond with the completed
    // operation. The server MAY choose to stream additional updates as
    // execution progresses, such as to provide an update as to the state of the
    // execution.
    auto WaitExecution(::grpc::ServerContext* context,
                       const ::bazel_re::WaitExecutionRequest* request,
                       ::grpc::ServerWriter<::google::longrunning::Operation>*
                           writer) -> ::grpc::Status override;

  private:
    StorageConfig const& storage_config_;
    Storage const& storage_;
    LocalApi const& api_;
    OperationCache op_cache_;
    Logger logger_{"execution-service"};

    [[nodiscard]] auto ToIExecutionAction(::bazel_re::Action const& action,
                                          ::bazel_re::Command const& command,
                                          bool legacy_client) const noexcept
        -> std::optional<IExecutionAction::Ptr>;

    [[nodiscard]] auto ToBazelExecuteResponse(
        gsl::not_null<LocalResponse*> const& local_response,
        bool legacy_client) const noexcept
        -> expected<::bazel_re::ExecuteResponse, std::string>;

    void WriteResponse(
        ::bazel_re::ExecuteResponse const& execute_response,
        ::grpc::ServerWriter<::google::longrunning::Operation>* writer,
        ::google::longrunning::Operation&& op) noexcept;
};

#endif  // EXECUTION_SERVER_HPP