Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion doc/source/using_config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1117,7 +1117,8 @@ are the lowest priority configuration.
Remote execution
~~~~~~~~~~~~~~~~
BuildStream supports building remotely using the
`Google Remote Execution API (REAPI). <https://github.com/bazelbuild/remote-apis>`_.
`Google Remote Execution API (REAPI) <https://github.com/bazelbuild/remote-apis>`_.
There is also limited support to build locally while still using a remote action cache.

You can configure the remote execution services globally in your user configuration
using the ``remote-execution`` key, like so:
Expand Down Expand Up @@ -1148,6 +1149,8 @@ Attributes
how to connect with the main *execution service*, this service is the main controlling
entity in a remote execution build cluster.

Skip the configuration of this service to enable local builds with a remote action cache.

* ``storage-service``

A :ref:`service configuration <user_config_remote_execution_service>` specifying
Expand All @@ -1172,6 +1175,15 @@ Attributes
This service is optional in a remote execution build cluster, if your remote
execution service provides an action cache, then you should configure it here.

For local builds with a remote action cache, set ``push`` to ``true`` to
upload action results to the server. By default, action results are downloaded
from the server but not uploaded.

If remote execution is not enabled, the remote action cache is currently only
used for REAPI clients in the sandbox, not for BuildStream element build jobs.
For more information about REAPI clients in the sandbox, see ``remote-apis-socket``
in the :ref:`sandbox configuration <format_sandbox>`.


.. _user_config_remote_execution_service:

Expand Down
2 changes: 1 addition & 1 deletion requirements/requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Jinja2 >= 2.10
importlib_metadata >= 3.6; python_version < "3.10"
packaging
pluginbase
protobuf <6.0dev,>=5.26.1
protobuf <6.0dev,>=5.29
psutil
ruamel.yaml >= 0.16.7
ruamel.yaml.clib >= 0.1.2
Expand Down
4 changes: 2 additions & 2 deletions requirements/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
click==8.1.7
grpcio==1.68.0
grpcio==1.69.0
Jinja2==3.1.4
packaging==24.2
pluginbase==1.0.1
protobuf==5.28.3
protobuf==5.29.5
psutil==6.1.0
ruamel.yaml==0.18.6
ruamel.yaml.clib==0.2.12
Expand Down
39 changes: 5 additions & 34 deletions src/buildstream/_assetcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from ._exceptions import AssetCacheError, RemoteError
from ._remotespec import RemoteSpec, RemoteType
from ._remote import BaseRemote
from ._protos.build.bazel.remote.asset.v1 import remote_asset_pb2, remote_asset_pb2_grpc
from ._protos.build.bazel.remote.asset.v1 import remote_asset_pb2
from ._protos.build.buildgrid import local_cas_pb2
from ._protos.google.rpc import code_pb2

Expand All @@ -37,30 +37,14 @@ def __init__(self, spec, casd):
self.fetch_service = None
self.push_service = None

def close(self):
self.fetch_service = None
self.push_service = None
super().close()

def _configure_protocols(self):
local_cas = self.casd.get_local_cas()
request = local_cas_pb2.GetInstanceNameForRemotesRequest()
self.spec.to_localcas_remote(request.remote_asset)
try:
response = local_cas.GetInstanceNameForRemotes(request)
self.instance_name = response.instance_name
self.fetch_service = self.casd.get_asset_fetch()
self.push_service = self.casd.get_asset_push()
except grpc.RpcError as e:
if e.code() == grpc.StatusCode.UNIMPLEMENTED or e.code() == grpc.StatusCode.INVALID_ARGUMENT:
# buildbox-casd is too old to support asset-only remotes.
# Fall back to direct connection.
self.instance_name = self.spec.instance_name
self.channel = self.spec.open_channel()
self.fetch_service = remote_asset_pb2_grpc.FetchStub(self.channel)
self.push_service = remote_asset_pb2_grpc.PushStub(self.channel)
else:
raise
response = local_cas.GetInstanceNameForRemotes(request)
self.instance_name = response.instance_name
self.fetch_service = self.casd.get_asset_fetch()
self.push_service = self.casd.get_asset_push()

# _check():
#
Expand Down Expand Up @@ -316,19 +300,6 @@ def __init__(self, context):

self._basedir = None

# release_resources():
#
# Release resources used by AssetCache.
#
def release_resources(self):

# Close all remotes and their gRPC channels
for remote in self._remotes.values():
if remote.index:
remote.index.close()
if remote.storage:
remote.storage.close()

# setup_remotes():
#
# Sets up which remotes to use
Expand Down
2 changes: 1 addition & 1 deletion src/buildstream/_cas/casdprocessmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
#
_REQUIRED_CASD_MAJOR = 1
_REQUIRED_CASD_MINOR = 2
_REQUIRED_CASD_MICRO = 0
_REQUIRED_CASD_MICRO = 6


# CASDProcessManager
Expand Down
11 changes: 1 addition & 10 deletions src/buildstream/_cas/casremote.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
# limitations under the License.
#

import grpc

from .._protos.google.rpc import code_pb2
from .._protos.build.buildgrid import local_cas_pb2

Expand Down Expand Up @@ -60,14 +58,7 @@ def _configure_protocols(self):
local_cas = self.cascache.get_local_cas()
request = local_cas_pb2.GetInstanceNameForRemotesRequest()
self.spec.to_localcas_remote(request.content_addressable_storage)
try:
response = local_cas.GetInstanceNameForRemotes(request)
except grpc.RpcError as e:
if e.code() == grpc.StatusCode.UNIMPLEMENTED:
raise CASRemoteError(
"Unsupported buildbox-casd version: GetInstanceNameForRemotes unimplemented"
) from e
raise
response = local_cas.GetInstanceNameForRemotes(request)
self.local_cas_instance_name = response.instance_name

# push_message():
Expand Down
9 changes: 0 additions & 9 deletions src/buildstream/_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,15 +244,6 @@ def __enter__(self) -> "Context":
# Called when exiting the with-statement context.
#
def __exit__(self, exc_type, exc_value, traceback) -> None:
if self._artifactcache:
self._artifactcache.release_resources()

if self._elementsourcescache:
self._elementsourcescache.release_resources()

if self._sourcecache:
self._sourcecache.release_resources()

if self._cascache:
self._cascache.release_resources()

Expand Down
3 changes: 2 additions & 1 deletion src/buildstream/_frontend/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,8 @@ def format_spec(spec):
text += "\n"
text += self.content_profile.fmt("Remote Execution Configuration\n", bold=True)
values = {}
values["Execution Service"] = format_spec(specs.exec_spec)
if specs.exec_spec:
values["Execution Service"] = format_spec(specs.exec_spec)
re_storage_spec = specs.storage_spec or context.remote_cache_spec
values["Storage Service"] = format_spec(re_storage_spec)
if specs.action_spec:
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from buildstream._protos.build.bazel.remote.asset.v1 import remote_asset_pb2 as build_dot_bazel_dot_remote_dot_asset_dot_v1_dot_remote__asset__pb2

GRPC_GENERATED_VERSION = '1.68.0'
GRPC_GENERATED_VERSION = '1.69.0'
GRPC_VERSION = grpc.__version__
_version_not_supported = False

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from buildstream._protos.build.bazel.remote.execution.v2 import remote_execution_pb2 as build_dot_bazel_dot_remote_dot_execution_dot_v2_dot_remote__execution__pb2
from buildstream._protos.google.longrunning import operations_pb2 as google_dot_longrunning_dot_operations__pb2

GRPC_GENERATED_VERSION = '1.68.0'
GRPC_GENERATED_VERSION = '1.69.0'
GRPC_VERSION = grpc.__version__
_version_not_supported = False

Expand Down
6 changes: 3 additions & 3 deletions src/buildstream/_protos/build/bazel/semver/semver_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import warnings


GRPC_GENERATED_VERSION = '1.68.0'
GRPC_GENERATED_VERSION = '1.69.0'
GRPC_VERSION = grpc.__version__
_version_not_supported = False

Expand Down
84 changes: 84 additions & 0 deletions src/buildstream/_protos/build/buildgrid/local_cas.proto
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,14 @@ package build.buildgrid;
import "build/bazel/remote/execution/v2/remote_execution.proto";
import "google/rpc/status.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/wrappers.proto";

service LocalContentAddressableStorage {
// Retrieves the configuration details for the local CAS server.
// This RPC allows clients to obtain information such as the hostname,
// user ID, and storage root of the local CAS server.
rpc GetLocalServerDetails(GetLocalServerDetailsRequest) returns (LocalServerDetails) {}

// Fetch blobs from a remote CAS to the local cache.
//
// This request is equivalent to ByteStream `Read` or `BatchReadBlobs`
Expand Down Expand Up @@ -158,6 +164,41 @@ service LocalContentAddressableStorage {
rpc GetLocalDiskUsage(GetLocalDiskUsageRequest) returns (GetLocalDiskUsageResponse) {}
}

// A request message for
// [LocalContentAddressableStorage.GetLocalServerDetails][build.buildgrid.v2.LocalContentAddressableStorage.GetLocalServerDetails].
message GetLocalServerDetailsRequest
{
// The instance of the execution system to operate against. A server may
// support multiple instances of the execution system (with their own
// workers, storage, caches, etc.). The server MAY require use of this
// field to select between them in an implementation-defined fashion,
// otherwise it can be omitted.
string instance_name = 1;
}

// A response message for
// [LocalContentAddressableStorage.GetLocalServerDetails][build.buildgrid.v2.LocalContentAddressableStorage.GetLocalServerDetails].
message LocalServerDetails
{
// The hostname where the local CAS server is running.
// This is used to determine if the client is running on the same host as
// the server. When running on the same host, you may access the
// storage_root directly.
string hostname = 1;

// The user ID (UID) which owns the local CAS process.
// This is used to determine if the client is running as the same user as
// the server. When running as the same user, hardlinking from the
// storage_root is dangerous.
int64 process_uid = 2;

// The absolute path to the local CAS cache directory.
// The storage_root contains locally stored blobs for digests in CAS.
// The local path to a blob has the following format:
// storage_root/objects/{hash[:2]}/{hash[2:]}
string storage_root = 3;
}

// A request message for
// [LocalContentAddressableStorage.FetchMissingBlobs][build.buildgrid.v2.LocalContentAddressableStorage.FetchMissingBlobs].
message FetchMissingBlobsRequest {
Expand Down Expand Up @@ -302,6 +343,36 @@ message StageTreeRequest {
// This will be used to ensure that the files have the right permissions
// for access without risking corruption of files in the local cache.
Credentials access_credentials = 4;

// The path, relative to the staging directory, where a server socket should
// be created for access to the Remote Execution API.
string remote_apis_socket_path = 7;

// The commands to run against the tree before it is cleaned.
// Each command must be an absolute path to an executable which can be run as
// `/path/to/command <path-to-stage-dir>`.
// The working directory of launched commands will be the same as the LocalCAS
// server process.
// The server SHOULD maintain an allowlist of commands that can be launched.
repeated string pre_unstage_commands = 5;

enum StagingMode {
// The staging mode is not specified. The LocalCAS server will use the default
// staging mode chosen from its configuration and environment.
DEFAULT = 0;

// The tree will be staged using libfuse.
FUSE = 1;

// The tree will be staged by copying or linking files from the local
// cache. The exact behavior depends on the filesystem, contents of the
// tree and the credentials of the client.
COPY_OR_LINK = 2;
}

// The staging mode to use for the tree.
// `INVALID_ARGUMENT` is returned if the server does not support the requested mode.
StagingMode staging_mode = 6;
}

// A response message for
Expand Down Expand Up @@ -356,6 +427,11 @@ message CaptureTreeRequest {
// and `bypass_local_cache` are set, the server MUST return an InvalidArgument
// error.
bool skip_upload = 8;

// The mask to apply to the files and directories being captured.
// For example, a mask of `0222` will result in contents being captured as read-only.
// This is only effective when the unix_mode property is captured.
google.protobuf.UInt32Value unix_mode_mask = 9;
}

// A response message for
Expand Down Expand Up @@ -419,6 +495,11 @@ message CaptureFilesRequest {
// and `bypass_local_cache` are set, the server MUST return an InvalidArgument
// error.
bool skip_upload = 7;

// The mask to apply to the files being captured.
// For example, a mask of `0222` will result in contents being captured as read-only.
// This is only effective when the unix_mode property is captured.
google.protobuf.UInt32Value unix_mode_mask = 8;
}

// A response message for
Expand Down Expand Up @@ -528,6 +609,9 @@ message Remote {

// How long to wait for a response from the remote.
google.protobuf.Duration request_timeout = 11;

// If set, don't upload any blobs, action results or assets to the remote.
bool read_only = 12;
}

// A request message for
Expand Down
Loading
Loading