From acc028bff073970473a1f7ead357daaaf1094798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Billeter?= Date: Fri, 27 Jun 2025 17:34:29 +0200 Subject: [PATCH 1/8] Require buildbox-casd 1.2.6 This makes it possible to drop the fallback to direct connections to remote servers. --- src/buildstream/_assetcache.py | 21 +++------- src/buildstream/_cas/casdprocessmanager.py | 2 +- src/buildstream/_cas/casremote.py | 11 +----- src/buildstream/_remote.py | 5 --- src/buildstream/_remotespec.py | 37 ------------------ .../sandbox/_sandboxbuildboxrun.py | 3 -- src/buildstream/sandbox/_sandboxremote.py | 39 +++++-------------- 7 files changed, 16 insertions(+), 102 deletions(-) diff --git a/src/buildstream/_assetcache.py b/src/buildstream/_assetcache.py index 59fe1506a..62fc8e6bd 100644 --- a/src/buildstream/_assetcache.py +++ b/src/buildstream/_assetcache.py @@ -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 @@ -46,21 +46,10 @@ 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(): # diff --git a/src/buildstream/_cas/casdprocessmanager.py b/src/buildstream/_cas/casdprocessmanager.py index fd1692d9f..5af7b2fac 100644 --- a/src/buildstream/_cas/casdprocessmanager.py +++ b/src/buildstream/_cas/casdprocessmanager.py @@ -47,7 +47,7 @@ # _REQUIRED_CASD_MAJOR = 1 _REQUIRED_CASD_MINOR = 2 -_REQUIRED_CASD_MICRO = 0 +_REQUIRED_CASD_MICRO = 6 # CASDProcessManager diff --git a/src/buildstream/_cas/casremote.py b/src/buildstream/_cas/casremote.py index e7d812167..ae4aa9004 100644 --- a/src/buildstream/_cas/casremote.py +++ b/src/buildstream/_cas/casremote.py @@ -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 @@ -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(): diff --git a/src/buildstream/_remote.py b/src/buildstream/_remote.py index 32584de09..27b6dde9b 100644 --- a/src/buildstream/_remote.py +++ b/src/buildstream/_remote.py @@ -34,7 +34,6 @@ class BaseRemote: def __init__(self, spec): self.spec = spec - self.channel = None self._initialized = False self._lock = threading.Lock() @@ -72,10 +71,6 @@ def init(self): self._initialized = True def close(self): - if self.channel: - self.channel.close() - self.channel = None - self._initialized = False # check(): diff --git a/src/buildstream/_remotespec.py b/src/buildstream/_remotespec.py index 3c9aa2fa3..1a22ee043 100644 --- a/src/buildstream/_remotespec.py +++ b/src/buildstream/_remotespec.py @@ -14,9 +14,6 @@ import os from typing import Optional, Tuple, List, cast -from urllib.parse import urlparse -import grpc -from grpc import ChannelCredentials, Channel from ._exceptions import LoadError, RemoteError from .exceptions import LoadErrorReason @@ -114,9 +111,6 @@ def __init__( self._client_cert: Optional[bytes] = None self._cred_files_loaded: bool = False - # The grpc credentials object - self._credentials: Optional[ChannelCredentials] = None - # Various connection parameters for grpc connection self._connection_config: Optional[MappingNode] = connection_config @@ -178,18 +172,6 @@ def client_cert(self) -> Optional[bytes]: self._load_credential_files() return self._client_cert - # credentials() - # - @property - def credentials(self) -> ChannelCredentials: - if not self._credentials: - self._credentials = grpc.ssl_channel_credentials( - root_certificates=self.server_cert, - private_key=self.client_key, - certificate_chain=self.client_cert, - ) - return self._credentials - # grpc keepalive time # @property @@ -222,25 +204,6 @@ def request_timeout(self) -> Optional[int]: return self._connection_config.get_int("request-timeout", None) return None - # open_channel() - # - # Opens a gRPC channel based on this spec. - # - def open_channel(self) -> Channel: - url = urlparse(self.url) - - if url.scheme == "http": - channel = grpc.insecure_channel("{}:{}".format(url.hostname, url.port or 80)) - elif url.scheme == "https": - channel = grpc.secure_channel("{}:{}".format(url.hostname, url.port or 443), self.credentials) - else: - message = "Only 'http' and 'https' protocols are supported, but '{}' was supplied.".format(url.scheme) - if self._spec_node: - message = "{}: {}".format(self._spec_node.get_provenance(), message) - raise RemoteError(message) - - return channel - # to_localcas_remote() # # Create a `LocalContentAddressableStorage.Remote` proto from the `RemoteSpec` object. diff --git a/src/buildstream/sandbox/_sandboxbuildboxrun.py b/src/buildstream/sandbox/_sandboxbuildboxrun.py index d4b49860a..d79f54b2f 100644 --- a/src/buildstream/sandbox/_sandboxbuildboxrun.py +++ b/src/buildstream/sandbox/_sandboxbuildboxrun.py @@ -47,9 +47,6 @@ def _setup(cls): if exit_code == 0: # buildbox-run --capabilities prints one capability per line cls._capabilities = set(output.split("\n")) - elif "Invalid option --capabilities" in output: - # buildbox-run is too old to support extra capabilities - cls._capabilities = set() else: # buildbox-run is not functional raise SandboxError( diff --git a/src/buildstream/sandbox/_sandboxremote.py b/src/buildstream/sandbox/_sandboxremote.py index 8abaa2b53..bb185b72b 100644 --- a/src/buildstream/sandbox/_sandboxremote.py +++ b/src/buildstream/sandbox/_sandboxremote.py @@ -21,11 +21,11 @@ from ._sandboxreapi import SandboxREAPI from .. import _signals from .._remote import BaseRemote -from .._protos.build.bazel.remote.execution.v2 import remote_execution_pb2, remote_execution_pb2_grpc +from .._protos.build.bazel.remote.execution.v2 import remote_execution_pb2 from .._protos.build.buildgrid import local_cas_pb2 from .._protos.google.rpc import code_pb2 from .._exceptions import BstError, SandboxError -from .._protos.google.longrunning import operations_pb2, operations_pb2_grpc +from .._protos.google.longrunning import operations_pb2 from .._cas import CASRemote @@ -46,21 +46,10 @@ def _configure_protocols(self): local_cas = self.casd.get_local_cas() request = local_cas_pb2.GetInstanceNameForRemotesRequest() self.spec.to_localcas_remote(request.execution) - try: - response = local_cas.GetInstanceNameForRemotes(request) - self.instance_name = response.instance_name - self.exec_service = self.casd.get_exec_service() - self.operations_service = self.casd.get_operations_service() - 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 execution service remotes. - # Fall back to direct connection. - self.instance_name = self.spec.instance_name - self.channel = self.spec.open_channel() - self.exec_service = remote_execution_pb2_grpc.ExecutionStub(self.channel) - self.operations_service = operations_pb2_grpc.OperationsStub(self.channel) - else: - raise + response = local_cas.GetInstanceNameForRemotes(request) + self.instance_name = response.instance_name + self.exec_service = self.casd.get_exec_service() + self.operations_service = self.casd.get_operations_service() class ActionCacheRemote(BaseRemote): @@ -78,19 +67,9 @@ def _configure_protocols(self): local_cas = self.casd.get_local_cas() request = local_cas_pb2.GetInstanceNameForRemotesRequest() self.spec.to_localcas_remote(request.action_cache) - try: - response = local_cas.GetInstanceNameForRemotes(request) - self.instance_name = response.instance_name - self.ac_service = self.casd.get_ac_service() - 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 action cache remotes. - # Fall back to direct connection. - self.instance_name = self.spec.instance_name - self.channel = self.spec.open_channel() - self.ac_service = remote_execution_pb2_grpc.ActionCacheStub(self.channel) - else: - raise + response = local_cas.GetInstanceNameForRemotes(request) + self.instance_name = response.instance_name + self.ac_service = self.casd.get_ac_service() # SandboxRemote() From 8e7b096ca28e4e699699760cf1a28f98d102178d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Billeter?= Date: Fri, 27 Jun 2025 18:17:45 +0200 Subject: [PATCH 2/8] sandbox: Create single buildbox-casd instance for remote execution --- src/buildstream/sandbox/_reremote.py | 52 ++++++++++ src/buildstream/sandbox/_sandboxremote.py | 110 ++++------------------ 2 files changed, 72 insertions(+), 90 deletions(-) create mode 100644 src/buildstream/sandbox/_reremote.py diff --git a/src/buildstream/sandbox/_reremote.py b/src/buildstream/sandbox/_reremote.py new file mode 100644 index 000000000..4c7773fec --- /dev/null +++ b/src/buildstream/sandbox/_reremote.py @@ -0,0 +1,52 @@ +# +# 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. +# + +from .._protos.build.buildgrid import local_cas_pb2 + +from .._cas import CASRemote + + +class RERemote(CASRemote): + def __init__(self, cas_spec, remote_execution_specs, cascache): + super().__init__(cas_spec, cascache) + + self.remote_execution_specs = remote_execution_specs + self.exec_service = None + self.operations_service = None + self.ac_service = None + + def close(self): + self.exec_service = None + self.operations_service = None + self.ac_service = None + super().close() + + def _configure_protocols(self): + local_cas = self.cascache.get_local_cas() + request = local_cas_pb2.GetInstanceNameForRemotesRequest() + if self.remote_execution_specs.storage_spec: + self.remote_execution_specs.storage_spec.to_localcas_remote(request.content_addressable_storage) + else: + self.spec.to_localcas_remote(request.content_addressable_storage) + if self.remote_execution_specs.exec_spec: + self.remote_execution_specs.exec_spec.to_localcas_remote(request.execution) + if self.remote_execution_specs.action_spec: + self.remote_execution_specs.action_spec.to_localcas_remote(request.action_cache) + response = local_cas.GetInstanceNameForRemotes(request) + self.local_cas_instance_name = response.instance_name + + casd = self.cascache.get_casd() + self.exec_service = casd.get_exec_service() + self.operations_service = casd.get_operations_service() + self.ac_service = casd.get_ac_service() diff --git a/src/buildstream/sandbox/_sandboxremote.py b/src/buildstream/sandbox/_sandboxremote.py index bb185b72b..dbd967a95 100644 --- a/src/buildstream/sandbox/_sandboxremote.py +++ b/src/buildstream/sandbox/_sandboxremote.py @@ -18,58 +18,13 @@ import grpc +from ._reremote import RERemote from ._sandboxreapi import SandboxREAPI from .. import _signals -from .._remote import BaseRemote from .._protos.build.bazel.remote.execution.v2 import remote_execution_pb2 -from .._protos.build.buildgrid import local_cas_pb2 from .._protos.google.rpc import code_pb2 from .._exceptions import BstError, SandboxError from .._protos.google.longrunning import operations_pb2 -from .._cas import CASRemote - - -class ExecutionRemote(BaseRemote): - def __init__(self, spec, casd): - super().__init__(spec) - self.casd = casd - self.instance_name = None - self.exec_service = None - self.operations_service = None - - def close(self): - self.exec_service = None - self.operations_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.execution) - response = local_cas.GetInstanceNameForRemotes(request) - self.instance_name = response.instance_name - self.exec_service = self.casd.get_exec_service() - self.operations_service = self.casd.get_operations_service() - - -class ActionCacheRemote(BaseRemote): - def __init__(self, spec, casd): - super().__init__(spec) - self.casd = casd - self.instance_name = None - self.ac_service = None - - def close(self): - self.ac_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.action_cache) - response = local_cas.GetInstanceNameForRemotes(request) - self.instance_name = response.instance_name - self.ac_service = self.casd.get_ac_service() # SandboxRemote() @@ -83,7 +38,6 @@ def __init__(self, *args, **kwargs): context = self._get_context() cascache = context.get_cascache() - casd = context.get_casd() specs = context.remote_execution_specs if specs is None: @@ -94,46 +48,26 @@ def __init__(self, *args, **kwargs): self.action_spec = specs.action_spec self.operation_name = None - if self.storage_spec: - self.own_storage_remote = True - self.storage_remote = CASRemote(self.storage_spec, cascache) - try: - self.storage_remote.init() - except grpc.RpcError as e: - raise SandboxError( - "Failed to contact remote execution CAS endpoint at {}: {}".format(self.storage_spec.url, e) - ) from e - else: - self.own_storage_remote = False - self.storage_remote = cascache.get_default_remote() - - self.exec_remote = ExecutionRemote(self.exec_spec, casd) + self.re_remote = RERemote(context.remote_cache_spec, specs, cascache) try: - self.exec_remote.init() + self.re_remote.init() except grpc.RpcError as e: - raise SandboxError( - "Failed to contact remote execution service at {}: {}".format(self.exec_spec.url, e) - ) from e - - if self.action_spec: - self.ac_remote = ActionCacheRemote(self.action_spec, casd) - try: - self.ac_remote.init() - except grpc.RpcError as e: - raise SandboxError( - "Failed to contact action cache service at {}: {}".format(self.action_spec.url, e) - ) from e - else: - self.ac_remote = None + urls = set() + if self.storage_spec: + urls.add(self.storage_spec.url) + urls.add(self.exec_spec.url) + if self.action_spec: + urls.add(self.action_spec.url) + raise SandboxError("Failed to contact remote execution endpoint at {}: {}".format(sorted(urls), e)) from e def run_remote_command(self, action_digest): # Sends an execution request to the remote execution server. # # This function blocks until it gets a response from the server. - stub = self.exec_remote.exec_service + stub = self.re_remote.exec_service request = remote_execution_pb2.ExecuteRequest( - instance_name=self.exec_remote.instance_name, action_digest=action_digest, skip_cache_lookup=False + instance_name=self.re_remote.local_cas_instance_name, action_digest=action_digest, skip_cache_lookup=False ) def __run_remote_command(stub, execute_request=None, running_operation=None): @@ -196,7 +130,7 @@ def cancel_operation(self): if self.operation_name is None: return - stub = self.exec_remote.operations_service + stub = self.re_remote.operations_service request = operations_pb2.CancelOperationRequest(name=str(self.operation_name)) try: @@ -220,7 +154,7 @@ def _fetch_missing_blobs(self, vdir): local_missing_blobs = cascache.missing_blobs(required_blobs) if local_missing_blobs: - cascache.fetch_blobs(self.storage_remote, local_missing_blobs) + cascache.fetch_blobs(self.re_remote, local_missing_blobs) def _execute_action(self, action, flags): stdout, stderr = self._get_output() @@ -232,7 +166,7 @@ def _execute_action(self, action, flags): action_digest = cascache.add_object(buffer=action.SerializeToString()) - casremote = self.storage_remote + casremote = self.re_remote # check action cache download and download if there action_result = self._check_action_cache(action_digest) @@ -312,13 +246,13 @@ def _check_action_cache(self, action_digest): # # Should return either the action response or None if not found, raise # Sandboxerror if other grpc error was raised - if not self.ac_remote: + if not self.action_spec: return None request = remote_execution_pb2.GetActionResultRequest( - instance_name=self.ac_remote.instance_name, action_digest=action_digest + instance_name=self.re_remote.local_cas_instance_name, action_digest=action_digest ) - stub = self.ac_remote.ac_service + stub = self.re_remote.ac_service try: result = stub.GetActionResult(request) except grpc.RpcError as e: @@ -355,9 +289,5 @@ def _extract_action_result(operation): return execution_response.result def _cleanup(self): - if self.ac_remote: - self.ac_remote.close() - if self.exec_remote: - self.exec_remote.close() - if self.own_storage_remote: - self.storage_remote.close() + if self.re_remote: + self.re_remote.close() From 2ddb26e30c18760f36b04b51de9b35845edfe4e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Billeter?= Date: Fri, 11 Jul 2025 12:48:30 +0200 Subject: [PATCH 3/8] Drop obsolete cleanup methods Without direct gRPC connections to remote servers, these cleanup methods are no longer needed. --- src/buildstream/_assetcache.py | 18 ------------------ src/buildstream/_context.py | 9 --------- src/buildstream/_remote.py | 6 ------ src/buildstream/sandbox/_reremote.py | 6 ------ src/buildstream/sandbox/_sandboxremote.py | 4 ---- src/buildstream/sandbox/sandbox.py | 9 +-------- 6 files changed, 1 insertion(+), 51 deletions(-) diff --git a/src/buildstream/_assetcache.py b/src/buildstream/_assetcache.py index 62fc8e6bd..df70fc2ff 100644 --- a/src/buildstream/_assetcache.py +++ b/src/buildstream/_assetcache.py @@ -37,11 +37,6 @@ 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() @@ -305,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 diff --git a/src/buildstream/_context.py b/src/buildstream/_context.py index 02942c8b0..d11c906d7 100644 --- a/src/buildstream/_context.py +++ b/src/buildstream/_context.py @@ -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() diff --git a/src/buildstream/_remote.py b/src/buildstream/_remote.py index 27b6dde9b..fe8c1dd88 100644 --- a/src/buildstream/_remote.py +++ b/src/buildstream/_remote.py @@ -44,7 +44,6 @@ def __enter__(self): return self def __exit__(self, _exc_type, _exc_value, traceback): - self.close() return False def __str__(self): @@ -70,9 +69,6 @@ def init(self): self._configure_protocols() self._initialized = True - def close(self): - self._initialized = False - # check(): # # Check if the remote is functional and has all the required @@ -92,8 +88,6 @@ def check(self): except grpc.RpcError as e: # str(e) is too verbose for errors reported to the user raise RemoteError("{}: {}".format(e.code().name, e.details())) - finally: - self.close() #################################################### # Abstract methods # diff --git a/src/buildstream/sandbox/_reremote.py b/src/buildstream/sandbox/_reremote.py index 4c7773fec..ca320ae7f 100644 --- a/src/buildstream/sandbox/_reremote.py +++ b/src/buildstream/sandbox/_reremote.py @@ -26,12 +26,6 @@ def __init__(self, cas_spec, remote_execution_specs, cascache): self.operations_service = None self.ac_service = None - def close(self): - self.exec_service = None - self.operations_service = None - self.ac_service = None - super().close() - def _configure_protocols(self): local_cas = self.cascache.get_local_cas() request = local_cas_pb2.GetInstanceNameForRemotesRequest() diff --git a/src/buildstream/sandbox/_sandboxremote.py b/src/buildstream/sandbox/_sandboxremote.py index dbd967a95..7d40088e8 100644 --- a/src/buildstream/sandbox/_sandboxremote.py +++ b/src/buildstream/sandbox/_sandboxremote.py @@ -287,7 +287,3 @@ def _extract_action_result(operation): raise SandboxError("Remote server failed at executing the build request.") return execution_response.result - - def _cleanup(self): - if self.re_remote: - self.re_remote.close() diff --git a/src/buildstream/sandbox/sandbox.py b/src/buildstream/sandbox/sandbox.py index 505cc5c1a..a503ac9f7 100644 --- a/src/buildstream/sandbox/sandbox.py +++ b/src/buildstream/sandbox/sandbox.py @@ -115,7 +115,7 @@ def __enter__(self) -> "Sandbox": # Called when exiting the with-statement context. # def __exit__(self, exc_type, exc_value, traceback) -> None: - self._cleanup() + pass def get_virtual_directory(self) -> Directory: """Fetches the sandbox root directory as a virtual Directory. @@ -268,13 +268,6 @@ def batch( # Abstract Methods for Sandbox implementations # ##################################################### - # _cleanup(): - # - # Abstract method to release resources when the sandbox is discarded - # - def _cleanup(self): - pass - # _run() # # Abstract method for running a single command From 9c4378ab2ec71b6b79ef90ee4bd950dd709a0310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Billeter?= Date: Fri, 4 Jul 2025 09:50:05 +0200 Subject: [PATCH 4/8] Update protobuf to 5.29.5 and grpcio to 1.69.0 This is the latest protobuf version before protobuf 6, which includes breaking changes. protobuf 5.29 is supported until 31 Mar 2026. --- requirements/requirements.in | 2 +- requirements/requirements.txt | 4 ++-- .../_protos/build/bazel/remote/asset/v1/remote_asset_pb2.py | 6 +++--- .../build/bazel/remote/asset/v1/remote_asset_pb2_grpc.py | 2 +- .../build/bazel/remote/execution/v2/remote_execution_pb2.py | 6 +++--- .../bazel/remote/execution/v2/remote_execution_pb2_grpc.py | 2 +- src/buildstream/_protos/build/bazel/semver/semver_pb2.py | 6 +++--- .../_protos/build/bazel/semver/semver_pb2_grpc.py | 2 +- src/buildstream/_protos/build/buildgrid/local_cas_pb2.py | 6 +++--- .../_protos/build/buildgrid/local_cas_pb2_grpc.py | 2 +- src/buildstream/_protos/buildstream/v2/artifact_pb2.py | 6 +++--- src/buildstream/_protos/buildstream/v2/artifact_pb2_grpc.py | 2 +- src/buildstream/_protos/buildstream/v2/source_pb2.py | 6 +++--- src/buildstream/_protos/buildstream/v2/source_pb2_grpc.py | 2 +- src/buildstream/_protos/google/api/annotations_pb2.py | 6 +++--- src/buildstream/_protos/google/api/annotations_pb2_grpc.py | 2 +- src/buildstream/_protos/google/api/http_pb2.py | 6 +++--- src/buildstream/_protos/google/api/http_pb2_grpc.py | 2 +- src/buildstream/_protos/google/bytestream/bytestream_pb2.py | 6 +++--- .../_protos/google/bytestream/bytestream_pb2_grpc.py | 2 +- .../_protos/google/longrunning/operations_pb2.py | 6 +++--- .../_protos/google/longrunning/operations_pb2_grpc.py | 2 +- src/buildstream/_protos/google/rpc/code_pb2.py | 6 +++--- src/buildstream/_protos/google/rpc/code_pb2_grpc.py | 2 +- src/buildstream/_protos/google/rpc/status_pb2.py | 6 +++--- src/buildstream/_protos/google/rpc/status_pb2_grpc.py | 2 +- 26 files changed, 51 insertions(+), 51 deletions(-) diff --git a/requirements/requirements.in b/requirements/requirements.in index 0143405be..5a53c918e 100644 --- a/requirements/requirements.in +++ b/requirements/requirements.in @@ -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 diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 910b71755..4a287f7ed 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -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 diff --git a/src/buildstream/_protos/build/bazel/remote/asset/v1/remote_asset_pb2.py b/src/buildstream/_protos/build/bazel/remote/asset/v1/remote_asset_pb2.py index ce8f6c62e..4f3335d00 100644 --- a/src/buildstream/_protos/build/bazel/remote/asset/v1/remote_asset_pb2.py +++ b/src/buildstream/_protos/build/bazel/remote/asset/v1/remote_asset_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: build/bazel/remote/asset/v1/remote_asset.proto -# Protobuf Python Version: 5.28.1 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -12,8 +12,8 @@ _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, 5, - 28, - 1, + 29, + 0, '', 'build/bazel/remote/asset/v1/remote_asset.proto' ) diff --git a/src/buildstream/_protos/build/bazel/remote/asset/v1/remote_asset_pb2_grpc.py b/src/buildstream/_protos/build/bazel/remote/asset/v1/remote_asset_pb2_grpc.py index ad2feca8e..71c73eff5 100644 --- a/src/buildstream/_protos/build/bazel/remote/asset/v1/remote_asset_pb2_grpc.py +++ b/src/buildstream/_protos/build/bazel/remote/asset/v1/remote_asset_pb2_grpc.py @@ -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 diff --git a/src/buildstream/_protos/build/bazel/remote/execution/v2/remote_execution_pb2.py b/src/buildstream/_protos/build/bazel/remote/execution/v2/remote_execution_pb2.py index 147dd4c31..569ec22aa 100644 --- a/src/buildstream/_protos/build/bazel/remote/execution/v2/remote_execution_pb2.py +++ b/src/buildstream/_protos/build/bazel/remote/execution/v2/remote_execution_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: build/bazel/remote/execution/v2/remote_execution.proto -# Protobuf Python Version: 5.28.1 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -12,8 +12,8 @@ _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, 5, - 28, - 1, + 29, + 0, '', 'build/bazel/remote/execution/v2/remote_execution.proto' ) diff --git a/src/buildstream/_protos/build/bazel/remote/execution/v2/remote_execution_pb2_grpc.py b/src/buildstream/_protos/build/bazel/remote/execution/v2/remote_execution_pb2_grpc.py index 2f8680032..a8e0d1e23 100644 --- a/src/buildstream/_protos/build/bazel/remote/execution/v2/remote_execution_pb2_grpc.py +++ b/src/buildstream/_protos/build/bazel/remote/execution/v2/remote_execution_pb2_grpc.py @@ -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 diff --git a/src/buildstream/_protos/build/bazel/semver/semver_pb2.py b/src/buildstream/_protos/build/bazel/semver/semver_pb2.py index 325c2ea33..79fea7670 100644 --- a/src/buildstream/_protos/build/bazel/semver/semver_pb2.py +++ b/src/buildstream/_protos/build/bazel/semver/semver_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: build/bazel/semver/semver.proto -# Protobuf Python Version: 5.28.1 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -12,8 +12,8 @@ _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, 5, - 28, - 1, + 29, + 0, '', 'build/bazel/semver/semver.proto' ) diff --git a/src/buildstream/_protos/build/bazel/semver/semver_pb2_grpc.py b/src/buildstream/_protos/build/bazel/semver/semver_pb2_grpc.py index 544c9fc9f..db6d404d9 100644 --- a/src/buildstream/_protos/build/bazel/semver/semver_pb2_grpc.py +++ b/src/buildstream/_protos/build/bazel/semver/semver_pb2_grpc.py @@ -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 diff --git a/src/buildstream/_protos/build/buildgrid/local_cas_pb2.py b/src/buildstream/_protos/build/buildgrid/local_cas_pb2.py index 13da13f42..2a2280f48 100644 --- a/src/buildstream/_protos/build/buildgrid/local_cas_pb2.py +++ b/src/buildstream/_protos/build/buildgrid/local_cas_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: build/buildgrid/local_cas.proto -# Protobuf Python Version: 5.28.1 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -12,8 +12,8 @@ _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, 5, - 28, - 1, + 29, + 0, '', 'build/buildgrid/local_cas.proto' ) diff --git a/src/buildstream/_protos/build/buildgrid/local_cas_pb2_grpc.py b/src/buildstream/_protos/build/buildgrid/local_cas_pb2_grpc.py index f890b7c02..f2ab82b2d 100644 --- a/src/buildstream/_protos/build/buildgrid/local_cas_pb2_grpc.py +++ b/src/buildstream/_protos/build/buildgrid/local_cas_pb2_grpc.py @@ -5,7 +5,7 @@ from buildstream._protos.build.buildgrid import local_cas_pb2 as build_dot_buildgrid_dot_local__cas__pb2 -GRPC_GENERATED_VERSION = '1.68.0' +GRPC_GENERATED_VERSION = '1.69.0' GRPC_VERSION = grpc.__version__ _version_not_supported = False diff --git a/src/buildstream/_protos/buildstream/v2/artifact_pb2.py b/src/buildstream/_protos/buildstream/v2/artifact_pb2.py index cce08c186..21cc67f4f 100644 --- a/src/buildstream/_protos/buildstream/v2/artifact_pb2.py +++ b/src/buildstream/_protos/buildstream/v2/artifact_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: buildstream/v2/artifact.proto -# Protobuf Python Version: 5.28.1 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -12,8 +12,8 @@ _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, 5, - 28, - 1, + 29, + 0, '', 'buildstream/v2/artifact.proto' ) diff --git a/src/buildstream/_protos/buildstream/v2/artifact_pb2_grpc.py b/src/buildstream/_protos/buildstream/v2/artifact_pb2_grpc.py index 546aaff8e..cbc767062 100644 --- a/src/buildstream/_protos/buildstream/v2/artifact_pb2_grpc.py +++ b/src/buildstream/_protos/buildstream/v2/artifact_pb2_grpc.py @@ -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 diff --git a/src/buildstream/_protos/buildstream/v2/source_pb2.py b/src/buildstream/_protos/buildstream/v2/source_pb2.py index 25a1dcc34..6e9b9dce8 100644 --- a/src/buildstream/_protos/buildstream/v2/source_pb2.py +++ b/src/buildstream/_protos/buildstream/v2/source_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: buildstream/v2/source.proto -# Protobuf Python Version: 5.28.1 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -12,8 +12,8 @@ _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, 5, - 28, - 1, + 29, + 0, '', 'buildstream/v2/source.proto' ) diff --git a/src/buildstream/_protos/buildstream/v2/source_pb2_grpc.py b/src/buildstream/_protos/buildstream/v2/source_pb2_grpc.py index 1a5d0b008..e675bf116 100644 --- a/src/buildstream/_protos/buildstream/v2/source_pb2_grpc.py +++ b/src/buildstream/_protos/buildstream/v2/source_pb2_grpc.py @@ -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 diff --git a/src/buildstream/_protos/google/api/annotations_pb2.py b/src/buildstream/_protos/google/api/annotations_pb2.py index 84e221d89..4853c79aa 100644 --- a/src/buildstream/_protos/google/api/annotations_pb2.py +++ b/src/buildstream/_protos/google/api/annotations_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: google/api/annotations.proto -# Protobuf Python Version: 5.28.1 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -12,8 +12,8 @@ _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, 5, - 28, - 1, + 29, + 0, '', 'google/api/annotations.proto' ) diff --git a/src/buildstream/_protos/google/api/annotations_pb2_grpc.py b/src/buildstream/_protos/google/api/annotations_pb2_grpc.py index 20414b108..54e739def 100644 --- a/src/buildstream/_protos/google/api/annotations_pb2_grpc.py +++ b/src/buildstream/_protos/google/api/annotations_pb2_grpc.py @@ -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 diff --git a/src/buildstream/_protos/google/api/http_pb2.py b/src/buildstream/_protos/google/api/http_pb2.py index 4c8494956..3f97534f9 100644 --- a/src/buildstream/_protos/google/api/http_pb2.py +++ b/src/buildstream/_protos/google/api/http_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: google/api/http.proto -# Protobuf Python Version: 5.28.1 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -12,8 +12,8 @@ _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, 5, - 28, - 1, + 29, + 0, '', 'google/api/http.proto' ) diff --git a/src/buildstream/_protos/google/api/http_pb2_grpc.py b/src/buildstream/_protos/google/api/http_pb2_grpc.py index 9c8eb1768..16f692651 100644 --- a/src/buildstream/_protos/google/api/http_pb2_grpc.py +++ b/src/buildstream/_protos/google/api/http_pb2_grpc.py @@ -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 diff --git a/src/buildstream/_protos/google/bytestream/bytestream_pb2.py b/src/buildstream/_protos/google/bytestream/bytestream_pb2.py index ab047f310..4184fa9ef 100644 --- a/src/buildstream/_protos/google/bytestream/bytestream_pb2.py +++ b/src/buildstream/_protos/google/bytestream/bytestream_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: google/bytestream/bytestream.proto -# Protobuf Python Version: 5.28.1 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -12,8 +12,8 @@ _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, 5, - 28, - 1, + 29, + 0, '', 'google/bytestream/bytestream.proto' ) diff --git a/src/buildstream/_protos/google/bytestream/bytestream_pb2_grpc.py b/src/buildstream/_protos/google/bytestream/bytestream_pb2_grpc.py index 164d68fef..8dc0d89f2 100644 --- a/src/buildstream/_protos/google/bytestream/bytestream_pb2_grpc.py +++ b/src/buildstream/_protos/google/bytestream/bytestream_pb2_grpc.py @@ -5,7 +5,7 @@ from buildstream._protos.google.bytestream import bytestream_pb2 as google_dot_bytestream_dot_bytestream__pb2 -GRPC_GENERATED_VERSION = '1.68.0' +GRPC_GENERATED_VERSION = '1.69.0' GRPC_VERSION = grpc.__version__ _version_not_supported = False diff --git a/src/buildstream/_protos/google/longrunning/operations_pb2.py b/src/buildstream/_protos/google/longrunning/operations_pb2.py index e1e27af54..30fbe66a6 100644 --- a/src/buildstream/_protos/google/longrunning/operations_pb2.py +++ b/src/buildstream/_protos/google/longrunning/operations_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: google/longrunning/operations.proto -# Protobuf Python Version: 5.28.1 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -12,8 +12,8 @@ _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, 5, - 28, - 1, + 29, + 0, '', 'google/longrunning/operations.proto' ) diff --git a/src/buildstream/_protos/google/longrunning/operations_pb2_grpc.py b/src/buildstream/_protos/google/longrunning/operations_pb2_grpc.py index fa1bfe4c9..e0a190752 100644 --- a/src/buildstream/_protos/google/longrunning/operations_pb2_grpc.py +++ b/src/buildstream/_protos/google/longrunning/operations_pb2_grpc.py @@ -6,7 +6,7 @@ from buildstream._protos.google.longrunning import operations_pb2 as google_dot_longrunning_dot_operations__pb2 from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 -GRPC_GENERATED_VERSION = '1.68.0' +GRPC_GENERATED_VERSION = '1.69.0' GRPC_VERSION = grpc.__version__ _version_not_supported = False diff --git a/src/buildstream/_protos/google/rpc/code_pb2.py b/src/buildstream/_protos/google/rpc/code_pb2.py index bb6a169b3..730260e3d 100644 --- a/src/buildstream/_protos/google/rpc/code_pb2.py +++ b/src/buildstream/_protos/google/rpc/code_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: google/rpc/code.proto -# Protobuf Python Version: 5.28.1 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -12,8 +12,8 @@ _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, 5, - 28, - 1, + 29, + 0, '', 'google/rpc/code.proto' ) diff --git a/src/buildstream/_protos/google/rpc/code_pb2_grpc.py b/src/buildstream/_protos/google/rpc/code_pb2_grpc.py index 1a1ef383c..4ee204bf4 100644 --- a/src/buildstream/_protos/google/rpc/code_pb2_grpc.py +++ b/src/buildstream/_protos/google/rpc/code_pb2_grpc.py @@ -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 diff --git a/src/buildstream/_protos/google/rpc/status_pb2.py b/src/buildstream/_protos/google/rpc/status_pb2.py index d89b20312..fef3e5840 100644 --- a/src/buildstream/_protos/google/rpc/status_pb2.py +++ b/src/buildstream/_protos/google/rpc/status_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: google/rpc/status.proto -# Protobuf Python Version: 5.28.1 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -12,8 +12,8 @@ _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, 5, - 28, - 1, + 29, + 0, '', 'google/rpc/status.proto' ) diff --git a/src/buildstream/_protos/google/rpc/status_pb2_grpc.py b/src/buildstream/_protos/google/rpc/status_pb2_grpc.py index da039705c..80f01f3ea 100644 --- a/src/buildstream/_protos/google/rpc/status_pb2_grpc.py +++ b/src/buildstream/_protos/google/rpc/status_pb2_grpc.py @@ -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 From ea41a51f72ce49bd929ece6174fa3e9d16c40cfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Billeter?= Date: Fri, 11 Jul 2025 13:26:22 +0200 Subject: [PATCH 5/8] _protos: Update local_cas.proto from buildbox --- .../_protos/build/buildgrid/local_cas.proto | 84 ++++++++++++ .../_protos/build/buildgrid/local_cas_pb2.py | 125 +++++++++--------- .../_protos/build/buildgrid/local_cas_pb2.pyi | 54 ++++++-- .../build/buildgrid/local_cas_pb2_grpc.py | 46 +++++++ 4 files changed, 242 insertions(+), 67 deletions(-) diff --git a/src/buildstream/_protos/build/buildgrid/local_cas.proto b/src/buildstream/_protos/build/buildgrid/local_cas.proto index a04c4aca4..0dc0cbc36 100644 --- a/src/buildstream/_protos/build/buildgrid/local_cas.proto +++ b/src/buildstream/_protos/build/buildgrid/local_cas.proto @@ -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` @@ -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 { @@ -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 `. + // 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 @@ -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 @@ -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 @@ -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 diff --git a/src/buildstream/_protos/build/buildgrid/local_cas_pb2.py b/src/buildstream/_protos/build/buildgrid/local_cas_pb2.py index 2a2280f48..30bebeae3 100644 --- a/src/buildstream/_protos/build/buildgrid/local_cas_pb2.py +++ b/src/buildstream/_protos/build/buildgrid/local_cas_pb2.py @@ -25,71 +25,78 @@ 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.rpc import status_pb2 as google_dot_rpc_dot_status__pb2 from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 +from google.protobuf import wrappers_pb2 as google_dot_protobuf_dot_wrappers__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1f\x62uild/buildgrid/local_cas.proto\x12\x0f\x62uild.buildgrid\x1a\x36\x62uild/bazel/remote/execution/v2/remote_execution.proto\x1a\x17google/rpc/status.proto\x1a\x1egoogle/protobuf/duration.proto\"p\n\x18\x46\x65tchMissingBlobsRequest\x12\x15\n\rinstance_name\x18\x01 \x01(\t\x12=\n\x0c\x62lob_digests\x18\x02 \x03(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\"\xcc\x01\n\x19\x46\x65tchMissingBlobsResponse\x12\x46\n\tresponses\x18\x01 \x03(\x0b\x32\x33.build.buildgrid.FetchMissingBlobsResponse.Response\x1ag\n\x08Response\x12\x37\n\x06\x64igest\x18\x01 \x01(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\x12\"\n\x06status\x18\x02 \x01(\x0b\x32\x12.google.rpc.Status\"q\n\x19UploadMissingBlobsRequest\x12\x15\n\rinstance_name\x18\x01 \x01(\t\x12=\n\x0c\x62lob_digests\x18\x02 \x03(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\"\xce\x01\n\x1aUploadMissingBlobsResponse\x12G\n\tresponses\x18\x01 \x03(\x0b\x32\x34.build.buildgrid.UploadMissingBlobsResponse.Response\x1ag\n\x08Response\x12\x37\n\x06\x64igest\x18\x01 \x01(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\x12\"\n\x06status\x18\x02 \x01(\x0b\x32\x12.google.rpc.Status\"\x81\x01\n\x10\x46\x65tchTreeRequest\x12\x15\n\rinstance_name\x18\x01 \x01(\t\x12<\n\x0broot_digest\x18\x02 \x01(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\x12\x18\n\x10\x66\x65tch_file_blobs\x18\x03 \x01(\x08\"\x13\n\x11\x46\x65tchTreeResponse\"h\n\x11UploadTreeRequest\x12\x15\n\rinstance_name\x18\x01 \x01(\t\x12<\n\x0broot_digest\x18\x02 \x01(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\"\x14\n\x12UploadTreeResponse\"\xe9\x01\n\x10StageTreeRequest\x12\x15\n\rinstance_name\x18\x01 \x01(\t\x12<\n\x0broot_digest\x18\x02 \x01(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\x12\x0c\n\x04path\x18\x03 \x01(\t\x12I\n\x12\x61\x63\x63\x65ss_credentials\x18\x04 \x01(\x0b\x32-.build.buildgrid.StageTreeRequest.Credentials\x1a\'\n\x0b\x43redentials\x12\x0b\n\x03uid\x18\x01 \x01(\x03\x12\x0b\n\x03gid\x18\x02 \x01(\x03\"!\n\x11StageTreeResponse\x12\x0c\n\x04path\x18\x01 \x01(\t\"\x86\x02\n\x12\x43\x61ptureTreeRequest\x12\x15\n\rinstance_name\x18\x01 \x01(\t\x12\x0c\n\x04root\x18\x06 \x01(\t\x12\x0c\n\x04path\x18\x02 \x03(\t\x12\x1a\n\x12\x62ypass_local_cache\x18\x03 \x01(\x08\x12\x17\n\x0fnode_properties\x18\x04 \x03(\t\x12\x12\n\nmove_files\x18\x05 \x01(\x08\x12_\n\x17output_directory_format\x18\x07 \x01(\x0e\x32>.build.bazel.remote.execution.v2.Command.OutputDirectoryFormat\x12\x13\n\x0bskip_upload\x18\x08 \x01(\x08\"\x9c\x02\n\x13\x43\x61ptureTreeResponse\x12@\n\tresponses\x18\x01 \x03(\x0b\x32-.build.buildgrid.CaptureTreeResponse.Response\x1a\xc2\x01\n\x08Response\x12\x0c\n\x04path\x18\x01 \x01(\t\x12<\n\x0btree_digest\x18\x02 \x01(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\x12\"\n\x06status\x18\x03 \x01(\x0b\x32\x12.google.rpc.Status\x12\x46\n\x15root_directory_digest\x18\x04 \x01(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\"\xa6\x01\n\x13\x43\x61ptureFilesRequest\x12\x15\n\rinstance_name\x18\x01 \x01(\t\x12\x0c\n\x04root\x18\x06 \x01(\t\x12\x0c\n\x04path\x18\x02 \x03(\t\x12\x1a\n\x12\x62ypass_local_cache\x18\x03 \x01(\x08\x12\x17\n\x0fnode_properties\x18\x04 \x03(\t\x12\x12\n\nmove_files\x18\x05 \x01(\x08\x12\x13\n\x0bskip_upload\x18\x07 \x01(\x08\"\xb8\x02\n\x14\x43\x61ptureFilesResponse\x12\x41\n\tresponses\x18\x01 \x03(\x0b\x32..build.buildgrid.CaptureFilesResponse.Response\x1a\xdc\x01\n\x08Response\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x37\n\x06\x64igest\x18\x02 \x01(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\x12\"\n\x06status\x18\x03 \x01(\x0b\x32\x12.google.rpc.Status\x12\x15\n\ris_executable\x18\x04 \x01(\x08\x12H\n\x0fnode_properties\x18\x06 \x01(\x0b\x32/.build.bazel.remote.execution.v2.NodePropertiesJ\x04\x08\x05\x10\x06\"\x83\x01\n\x1fGetInstanceNameForRemoteRequest\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x15\n\rinstance_name\x18\x02 \x01(\t\x12\x13\n\x0bserver_cert\x18\x03 \x01(\x0c\x12\x12\n\nclient_key\x18\x04 \x01(\x0c\x12\x13\n\x0b\x63lient_cert\x18\x05 \x01(\x0c\"9\n GetInstanceNameForRemoteResponse\x12\x15\n\rinstance_name\x18\x01 \x01(\t\"\xf2\x02\n\x06Remote\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x15\n\rinstance_name\x18\x02 \x01(\t\x12\x13\n\x0bserver_cert\x18\x03 \x01(\x0c\x12\x12\n\nclient_key\x18\x04 \x01(\x0c\x12\x13\n\x0b\x63lient_cert\x18\x05 \x01(\x0c\x12\x19\n\x11\x61\x63\x63\x65ss_token_path\x18\x07 \x01(\t\x12?\n\x1c\x61\x63\x63\x65ss_token_reload_interval\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x31\n\x0ekeepalive_time\x18\x06 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x13\n\x0bretry_limit\x18\t \x01(\x03\x12.\n\x0bretry_delay\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x32\n\x0frequest_timeout\x18\x0b \x01(\x0b\x32\x19.google.protobuf.Duration\"\x81\x02\n GetInstanceNameForRemotesRequest\x12\x15\n\rinstance_name\x18\x03 \x01(\t\x12<\n\x1b\x63ontent_addressable_storage\x18\x01 \x01(\x0b\x32\x17.build.buildgrid.Remote\x12-\n\x0cremote_asset\x18\x02 \x01(\x0b\x32\x17.build.buildgrid.Remote\x12-\n\x0c\x61\x63tion_cache\x18\x04 \x01(\x0b\x32\x17.build.buildgrid.Remote\x12*\n\texecution\x18\x05 \x01(\x0b\x32\x17.build.buildgrid.Remote\":\n!GetInstanceNameForRemotesResponse\x12\x15\n\rinstance_name\x18\x01 \x01(\t\"I\n\"GetInstanceNameForNamespaceRequest\x12\x15\n\rinstance_name\x18\x01 \x01(\t\x12\x0c\n\x04root\x18\x02 \x01(\t\"<\n#GetInstanceNameForNamespaceResponse\x12\x15\n\rinstance_name\x18\x01 \x01(\t\"\x1a\n\x18GetLocalDiskUsageRequest\"D\n\x19GetLocalDiskUsageResponse\x12\x12\n\nsize_bytes\x18\x01 \x01(\x03\x12\x13\n\x0bquota_bytes\x18\x02 \x01(\x03\x32\xc9\t\n\x1eLocalContentAddressableStorage\x12l\n\x11\x46\x65tchMissingBlobs\x12).build.buildgrid.FetchMissingBlobsRequest\x1a*.build.buildgrid.FetchMissingBlobsResponse\"\x00\x12o\n\x12UploadMissingBlobs\x12*.build.buildgrid.UploadMissingBlobsRequest\x1a+.build.buildgrid.UploadMissingBlobsResponse\"\x00\x12T\n\tFetchTree\x12!.build.buildgrid.FetchTreeRequest\x1a\".build.buildgrid.FetchTreeResponse\"\x00\x12W\n\nUploadTree\x12\".build.buildgrid.UploadTreeRequest\x1a#.build.buildgrid.UploadTreeResponse\"\x00\x12X\n\tStageTree\x12!.build.buildgrid.StageTreeRequest\x1a\".build.buildgrid.StageTreeResponse\"\x00(\x01\x30\x01\x12Z\n\x0b\x43\x61ptureTree\x12#.build.buildgrid.CaptureTreeRequest\x1a$.build.buildgrid.CaptureTreeResponse\"\x00\x12]\n\x0c\x43\x61ptureFiles\x12$.build.buildgrid.CaptureFilesRequest\x1a%.build.buildgrid.CaptureFilesResponse\"\x00\x12\x81\x01\n\x18GetInstanceNameForRemote\x12\x30.build.buildgrid.GetInstanceNameForRemoteRequest\x1a\x31.build.buildgrid.GetInstanceNameForRemoteResponse\"\x00\x12\x84\x01\n\x19GetInstanceNameForRemotes\x12\x31.build.buildgrid.GetInstanceNameForRemotesRequest\x1a\x32.build.buildgrid.GetInstanceNameForRemotesResponse\"\x00\x12\x8a\x01\n\x1bGetInstanceNameForNamespace\x12\x33.build.buildgrid.GetInstanceNameForNamespaceRequest\x1a\x34.build.buildgrid.GetInstanceNameForNamespaceResponse\"\x00\x12l\n\x11GetLocalDiskUsage\x12).build.buildgrid.GetLocalDiskUsageRequest\x1a*.build.buildgrid.GetLocalDiskUsageResponse\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1f\x62uild/buildgrid/local_cas.proto\x12\x0f\x62uild.buildgrid\x1a\x36\x62uild/bazel/remote/execution/v2/remote_execution.proto\x1a\x17google/rpc/status.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1egoogle/protobuf/wrappers.proto\"5\n\x1cGetLocalServerDetailsRequest\x12\x15\n\rinstance_name\x18\x01 \x01(\t\"Q\n\x12LocalServerDetails\x12\x10\n\x08hostname\x18\x01 \x01(\t\x12\x13\n\x0bprocess_uid\x18\x02 \x01(\x03\x12\x14\n\x0cstorage_root\x18\x03 \x01(\t\"p\n\x18\x46\x65tchMissingBlobsRequest\x12\x15\n\rinstance_name\x18\x01 \x01(\t\x12=\n\x0c\x62lob_digests\x18\x02 \x03(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\"\xcc\x01\n\x19\x46\x65tchMissingBlobsResponse\x12\x46\n\tresponses\x18\x01 \x03(\x0b\x32\x33.build.buildgrid.FetchMissingBlobsResponse.Response\x1ag\n\x08Response\x12\x37\n\x06\x64igest\x18\x01 \x01(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\x12\"\n\x06status\x18\x02 \x01(\x0b\x32\x12.google.rpc.Status\"q\n\x19UploadMissingBlobsRequest\x12\x15\n\rinstance_name\x18\x01 \x01(\t\x12=\n\x0c\x62lob_digests\x18\x02 \x03(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\"\xce\x01\n\x1aUploadMissingBlobsResponse\x12G\n\tresponses\x18\x01 \x03(\x0b\x32\x34.build.buildgrid.UploadMissingBlobsResponse.Response\x1ag\n\x08Response\x12\x37\n\x06\x64igest\x18\x01 \x01(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\x12\"\n\x06status\x18\x02 \x01(\x0b\x32\x12.google.rpc.Status\"\x81\x01\n\x10\x46\x65tchTreeRequest\x12\x15\n\rinstance_name\x18\x01 \x01(\t\x12<\n\x0broot_digest\x18\x02 \x01(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\x12\x18\n\x10\x66\x65tch_file_blobs\x18\x03 \x01(\x08\"\x13\n\x11\x46\x65tchTreeResponse\"h\n\x11UploadTreeRequest\x12\x15\n\rinstance_name\x18\x01 \x01(\t\x12<\n\x0broot_digest\x18\x02 \x01(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\"\x14\n\x12UploadTreeResponse\"\xa5\x03\n\x10StageTreeRequest\x12\x15\n\rinstance_name\x18\x01 \x01(\t\x12<\n\x0broot_digest\x18\x02 \x01(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\x12\x0c\n\x04path\x18\x03 \x01(\t\x12I\n\x12\x61\x63\x63\x65ss_credentials\x18\x04 \x01(\x0b\x32-.build.buildgrid.StageTreeRequest.Credentials\x12\x1f\n\x17remote_apis_socket_path\x18\x07 \x01(\t\x12\x1c\n\x14pre_unstage_commands\x18\x05 \x03(\t\x12\x43\n\x0cstaging_mode\x18\x06 \x01(\x0e\x32-.build.buildgrid.StageTreeRequest.StagingMode\x1a\'\n\x0b\x43redentials\x12\x0b\n\x03uid\x18\x01 \x01(\x03\x12\x0b\n\x03gid\x18\x02 \x01(\x03\"6\n\x0bStagingMode\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x00\x12\x08\n\x04\x46USE\x10\x01\x12\x10\n\x0c\x43OPY_OR_LINK\x10\x02\"!\n\x11StageTreeResponse\x12\x0c\n\x04path\x18\x01 \x01(\t\"\xbc\x02\n\x12\x43\x61ptureTreeRequest\x12\x15\n\rinstance_name\x18\x01 \x01(\t\x12\x0c\n\x04root\x18\x06 \x01(\t\x12\x0c\n\x04path\x18\x02 \x03(\t\x12\x1a\n\x12\x62ypass_local_cache\x18\x03 \x01(\x08\x12\x17\n\x0fnode_properties\x18\x04 \x03(\t\x12\x12\n\nmove_files\x18\x05 \x01(\x08\x12_\n\x17output_directory_format\x18\x07 \x01(\x0e\x32>.build.bazel.remote.execution.v2.Command.OutputDirectoryFormat\x12\x13\n\x0bskip_upload\x18\x08 \x01(\x08\x12\x34\n\x0eunix_mode_mask\x18\t \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\"\x9c\x02\n\x13\x43\x61ptureTreeResponse\x12@\n\tresponses\x18\x01 \x03(\x0b\x32-.build.buildgrid.CaptureTreeResponse.Response\x1a\xc2\x01\n\x08Response\x12\x0c\n\x04path\x18\x01 \x01(\t\x12<\n\x0btree_digest\x18\x02 \x01(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\x12\"\n\x06status\x18\x03 \x01(\x0b\x32\x12.google.rpc.Status\x12\x46\n\x15root_directory_digest\x18\x04 \x01(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\"\xdc\x01\n\x13\x43\x61ptureFilesRequest\x12\x15\n\rinstance_name\x18\x01 \x01(\t\x12\x0c\n\x04root\x18\x06 \x01(\t\x12\x0c\n\x04path\x18\x02 \x03(\t\x12\x1a\n\x12\x62ypass_local_cache\x18\x03 \x01(\x08\x12\x17\n\x0fnode_properties\x18\x04 \x03(\t\x12\x12\n\nmove_files\x18\x05 \x01(\x08\x12\x13\n\x0bskip_upload\x18\x07 \x01(\x08\x12\x34\n\x0eunix_mode_mask\x18\x08 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\"\xb8\x02\n\x14\x43\x61ptureFilesResponse\x12\x41\n\tresponses\x18\x01 \x03(\x0b\x32..build.buildgrid.CaptureFilesResponse.Response\x1a\xdc\x01\n\x08Response\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x37\n\x06\x64igest\x18\x02 \x01(\x0b\x32\'.build.bazel.remote.execution.v2.Digest\x12\"\n\x06status\x18\x03 \x01(\x0b\x32\x12.google.rpc.Status\x12\x15\n\ris_executable\x18\x04 \x01(\x08\x12H\n\x0fnode_properties\x18\x06 \x01(\x0b\x32/.build.bazel.remote.execution.v2.NodePropertiesJ\x04\x08\x05\x10\x06\"\x83\x01\n\x1fGetInstanceNameForRemoteRequest\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x15\n\rinstance_name\x18\x02 \x01(\t\x12\x13\n\x0bserver_cert\x18\x03 \x01(\x0c\x12\x12\n\nclient_key\x18\x04 \x01(\x0c\x12\x13\n\x0b\x63lient_cert\x18\x05 \x01(\x0c\"9\n GetInstanceNameForRemoteResponse\x12\x15\n\rinstance_name\x18\x01 \x01(\t\"\x85\x03\n\x06Remote\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x15\n\rinstance_name\x18\x02 \x01(\t\x12\x13\n\x0bserver_cert\x18\x03 \x01(\x0c\x12\x12\n\nclient_key\x18\x04 \x01(\x0c\x12\x13\n\x0b\x63lient_cert\x18\x05 \x01(\x0c\x12\x19\n\x11\x61\x63\x63\x65ss_token_path\x18\x07 \x01(\t\x12?\n\x1c\x61\x63\x63\x65ss_token_reload_interval\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x31\n\x0ekeepalive_time\x18\x06 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x13\n\x0bretry_limit\x18\t \x01(\x03\x12.\n\x0bretry_delay\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x32\n\x0frequest_timeout\x18\x0b \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x11\n\tread_only\x18\x0c \x01(\x08\"\x81\x02\n GetInstanceNameForRemotesRequest\x12\x15\n\rinstance_name\x18\x03 \x01(\t\x12<\n\x1b\x63ontent_addressable_storage\x18\x01 \x01(\x0b\x32\x17.build.buildgrid.Remote\x12-\n\x0cremote_asset\x18\x02 \x01(\x0b\x32\x17.build.buildgrid.Remote\x12-\n\x0c\x61\x63tion_cache\x18\x04 \x01(\x0b\x32\x17.build.buildgrid.Remote\x12*\n\texecution\x18\x05 \x01(\x0b\x32\x17.build.buildgrid.Remote\":\n!GetInstanceNameForRemotesResponse\x12\x15\n\rinstance_name\x18\x01 \x01(\t\"I\n\"GetInstanceNameForNamespaceRequest\x12\x15\n\rinstance_name\x18\x01 \x01(\t\x12\x0c\n\x04root\x18\x02 \x01(\t\"<\n#GetInstanceNameForNamespaceResponse\x12\x15\n\rinstance_name\x18\x01 \x01(\t\"\x1a\n\x18GetLocalDiskUsageRequest\"D\n\x19GetLocalDiskUsageResponse\x12\x12\n\nsize_bytes\x18\x01 \x01(\x03\x12\x13\n\x0bquota_bytes\x18\x02 \x01(\x03\x32\xb8\n\n\x1eLocalContentAddressableStorage\x12m\n\x15GetLocalServerDetails\x12-.build.buildgrid.GetLocalServerDetailsRequest\x1a#.build.buildgrid.LocalServerDetails\"\x00\x12l\n\x11\x46\x65tchMissingBlobs\x12).build.buildgrid.FetchMissingBlobsRequest\x1a*.build.buildgrid.FetchMissingBlobsResponse\"\x00\x12o\n\x12UploadMissingBlobs\x12*.build.buildgrid.UploadMissingBlobsRequest\x1a+.build.buildgrid.UploadMissingBlobsResponse\"\x00\x12T\n\tFetchTree\x12!.build.buildgrid.FetchTreeRequest\x1a\".build.buildgrid.FetchTreeResponse\"\x00\x12W\n\nUploadTree\x12\".build.buildgrid.UploadTreeRequest\x1a#.build.buildgrid.UploadTreeResponse\"\x00\x12X\n\tStageTree\x12!.build.buildgrid.StageTreeRequest\x1a\".build.buildgrid.StageTreeResponse\"\x00(\x01\x30\x01\x12Z\n\x0b\x43\x61ptureTree\x12#.build.buildgrid.CaptureTreeRequest\x1a$.build.buildgrid.CaptureTreeResponse\"\x00\x12]\n\x0c\x43\x61ptureFiles\x12$.build.buildgrid.CaptureFilesRequest\x1a%.build.buildgrid.CaptureFilesResponse\"\x00\x12\x81\x01\n\x18GetInstanceNameForRemote\x12\x30.build.buildgrid.GetInstanceNameForRemoteRequest\x1a\x31.build.buildgrid.GetInstanceNameForRemoteResponse\"\x00\x12\x84\x01\n\x19GetInstanceNameForRemotes\x12\x31.build.buildgrid.GetInstanceNameForRemotesRequest\x1a\x32.build.buildgrid.GetInstanceNameForRemotesResponse\"\x00\x12\x8a\x01\n\x1bGetInstanceNameForNamespace\x12\x33.build.buildgrid.GetInstanceNameForNamespaceRequest\x1a\x34.build.buildgrid.GetInstanceNameForNamespaceResponse\"\x00\x12l\n\x11GetLocalDiskUsage\x12).build.buildgrid.GetLocalDiskUsageRequest\x1a*.build.buildgrid.GetLocalDiskUsageResponse\"\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'build.buildgrid.local_cas_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: DESCRIPTOR._loaded_options = None - _globals['_FETCHMISSINGBLOBSREQUEST']._serialized_start=165 - _globals['_FETCHMISSINGBLOBSREQUEST']._serialized_end=277 - _globals['_FETCHMISSINGBLOBSRESPONSE']._serialized_start=280 - _globals['_FETCHMISSINGBLOBSRESPONSE']._serialized_end=484 - _globals['_FETCHMISSINGBLOBSRESPONSE_RESPONSE']._serialized_start=381 - _globals['_FETCHMISSINGBLOBSRESPONSE_RESPONSE']._serialized_end=484 - _globals['_UPLOADMISSINGBLOBSREQUEST']._serialized_start=486 - _globals['_UPLOADMISSINGBLOBSREQUEST']._serialized_end=599 - _globals['_UPLOADMISSINGBLOBSRESPONSE']._serialized_start=602 - _globals['_UPLOADMISSINGBLOBSRESPONSE']._serialized_end=808 - _globals['_UPLOADMISSINGBLOBSRESPONSE_RESPONSE']._serialized_start=381 - _globals['_UPLOADMISSINGBLOBSRESPONSE_RESPONSE']._serialized_end=484 - _globals['_FETCHTREEREQUEST']._serialized_start=811 - _globals['_FETCHTREEREQUEST']._serialized_end=940 - _globals['_FETCHTREERESPONSE']._serialized_start=942 - _globals['_FETCHTREERESPONSE']._serialized_end=961 - _globals['_UPLOADTREEREQUEST']._serialized_start=963 - _globals['_UPLOADTREEREQUEST']._serialized_end=1067 - _globals['_UPLOADTREERESPONSE']._serialized_start=1069 - _globals['_UPLOADTREERESPONSE']._serialized_end=1089 - _globals['_STAGETREEREQUEST']._serialized_start=1092 - _globals['_STAGETREEREQUEST']._serialized_end=1325 - _globals['_STAGETREEREQUEST_CREDENTIALS']._serialized_start=1286 - _globals['_STAGETREEREQUEST_CREDENTIALS']._serialized_end=1325 - _globals['_STAGETREERESPONSE']._serialized_start=1327 - _globals['_STAGETREERESPONSE']._serialized_end=1360 - _globals['_CAPTURETREEREQUEST']._serialized_start=1363 - _globals['_CAPTURETREEREQUEST']._serialized_end=1625 - _globals['_CAPTURETREERESPONSE']._serialized_start=1628 - _globals['_CAPTURETREERESPONSE']._serialized_end=1912 - _globals['_CAPTURETREERESPONSE_RESPONSE']._serialized_start=1718 - _globals['_CAPTURETREERESPONSE_RESPONSE']._serialized_end=1912 - _globals['_CAPTUREFILESREQUEST']._serialized_start=1915 - _globals['_CAPTUREFILESREQUEST']._serialized_end=2081 - _globals['_CAPTUREFILESRESPONSE']._serialized_start=2084 - _globals['_CAPTUREFILESRESPONSE']._serialized_end=2396 - _globals['_CAPTUREFILESRESPONSE_RESPONSE']._serialized_start=2176 - _globals['_CAPTUREFILESRESPONSE_RESPONSE']._serialized_end=2396 - _globals['_GETINSTANCENAMEFORREMOTEREQUEST']._serialized_start=2399 - _globals['_GETINSTANCENAMEFORREMOTEREQUEST']._serialized_end=2530 - _globals['_GETINSTANCENAMEFORREMOTERESPONSE']._serialized_start=2532 - _globals['_GETINSTANCENAMEFORREMOTERESPONSE']._serialized_end=2589 - _globals['_REMOTE']._serialized_start=2592 - _globals['_REMOTE']._serialized_end=2962 - _globals['_GETINSTANCENAMEFORREMOTESREQUEST']._serialized_start=2965 - _globals['_GETINSTANCENAMEFORREMOTESREQUEST']._serialized_end=3222 - _globals['_GETINSTANCENAMEFORREMOTESRESPONSE']._serialized_start=3224 - _globals['_GETINSTANCENAMEFORREMOTESRESPONSE']._serialized_end=3282 - _globals['_GETINSTANCENAMEFORNAMESPACEREQUEST']._serialized_start=3284 - _globals['_GETINSTANCENAMEFORNAMESPACEREQUEST']._serialized_end=3357 - _globals['_GETINSTANCENAMEFORNAMESPACERESPONSE']._serialized_start=3359 - _globals['_GETINSTANCENAMEFORNAMESPACERESPONSE']._serialized_end=3419 - _globals['_GETLOCALDISKUSAGEREQUEST']._serialized_start=3421 - _globals['_GETLOCALDISKUSAGEREQUEST']._serialized_end=3447 - _globals['_GETLOCALDISKUSAGERESPONSE']._serialized_start=3449 - _globals['_GETLOCALDISKUSAGERESPONSE']._serialized_end=3517 - _globals['_LOCALCONTENTADDRESSABLESTORAGE']._serialized_start=3520 - _globals['_LOCALCONTENTADDRESSABLESTORAGE']._serialized_end=4745 + _globals['_GETLOCALSERVERDETAILSREQUEST']._serialized_start=197 + _globals['_GETLOCALSERVERDETAILSREQUEST']._serialized_end=250 + _globals['_LOCALSERVERDETAILS']._serialized_start=252 + _globals['_LOCALSERVERDETAILS']._serialized_end=333 + _globals['_FETCHMISSINGBLOBSREQUEST']._serialized_start=335 + _globals['_FETCHMISSINGBLOBSREQUEST']._serialized_end=447 + _globals['_FETCHMISSINGBLOBSRESPONSE']._serialized_start=450 + _globals['_FETCHMISSINGBLOBSRESPONSE']._serialized_end=654 + _globals['_FETCHMISSINGBLOBSRESPONSE_RESPONSE']._serialized_start=551 + _globals['_FETCHMISSINGBLOBSRESPONSE_RESPONSE']._serialized_end=654 + _globals['_UPLOADMISSINGBLOBSREQUEST']._serialized_start=656 + _globals['_UPLOADMISSINGBLOBSREQUEST']._serialized_end=769 + _globals['_UPLOADMISSINGBLOBSRESPONSE']._serialized_start=772 + _globals['_UPLOADMISSINGBLOBSRESPONSE']._serialized_end=978 + _globals['_UPLOADMISSINGBLOBSRESPONSE_RESPONSE']._serialized_start=551 + _globals['_UPLOADMISSINGBLOBSRESPONSE_RESPONSE']._serialized_end=654 + _globals['_FETCHTREEREQUEST']._serialized_start=981 + _globals['_FETCHTREEREQUEST']._serialized_end=1110 + _globals['_FETCHTREERESPONSE']._serialized_start=1112 + _globals['_FETCHTREERESPONSE']._serialized_end=1131 + _globals['_UPLOADTREEREQUEST']._serialized_start=1133 + _globals['_UPLOADTREEREQUEST']._serialized_end=1237 + _globals['_UPLOADTREERESPONSE']._serialized_start=1239 + _globals['_UPLOADTREERESPONSE']._serialized_end=1259 + _globals['_STAGETREEREQUEST']._serialized_start=1262 + _globals['_STAGETREEREQUEST']._serialized_end=1683 + _globals['_STAGETREEREQUEST_CREDENTIALS']._serialized_start=1588 + _globals['_STAGETREEREQUEST_CREDENTIALS']._serialized_end=1627 + _globals['_STAGETREEREQUEST_STAGINGMODE']._serialized_start=1629 + _globals['_STAGETREEREQUEST_STAGINGMODE']._serialized_end=1683 + _globals['_STAGETREERESPONSE']._serialized_start=1685 + _globals['_STAGETREERESPONSE']._serialized_end=1718 + _globals['_CAPTURETREEREQUEST']._serialized_start=1721 + _globals['_CAPTURETREEREQUEST']._serialized_end=2037 + _globals['_CAPTURETREERESPONSE']._serialized_start=2040 + _globals['_CAPTURETREERESPONSE']._serialized_end=2324 + _globals['_CAPTURETREERESPONSE_RESPONSE']._serialized_start=2130 + _globals['_CAPTURETREERESPONSE_RESPONSE']._serialized_end=2324 + _globals['_CAPTUREFILESREQUEST']._serialized_start=2327 + _globals['_CAPTUREFILESREQUEST']._serialized_end=2547 + _globals['_CAPTUREFILESRESPONSE']._serialized_start=2550 + _globals['_CAPTUREFILESRESPONSE']._serialized_end=2862 + _globals['_CAPTUREFILESRESPONSE_RESPONSE']._serialized_start=2642 + _globals['_CAPTUREFILESRESPONSE_RESPONSE']._serialized_end=2862 + _globals['_GETINSTANCENAMEFORREMOTEREQUEST']._serialized_start=2865 + _globals['_GETINSTANCENAMEFORREMOTEREQUEST']._serialized_end=2996 + _globals['_GETINSTANCENAMEFORREMOTERESPONSE']._serialized_start=2998 + _globals['_GETINSTANCENAMEFORREMOTERESPONSE']._serialized_end=3055 + _globals['_REMOTE']._serialized_start=3058 + _globals['_REMOTE']._serialized_end=3447 + _globals['_GETINSTANCENAMEFORREMOTESREQUEST']._serialized_start=3450 + _globals['_GETINSTANCENAMEFORREMOTESREQUEST']._serialized_end=3707 + _globals['_GETINSTANCENAMEFORREMOTESRESPONSE']._serialized_start=3709 + _globals['_GETINSTANCENAMEFORREMOTESRESPONSE']._serialized_end=3767 + _globals['_GETINSTANCENAMEFORNAMESPACEREQUEST']._serialized_start=3769 + _globals['_GETINSTANCENAMEFORNAMESPACEREQUEST']._serialized_end=3842 + _globals['_GETINSTANCENAMEFORNAMESPACERESPONSE']._serialized_start=3844 + _globals['_GETINSTANCENAMEFORNAMESPACERESPONSE']._serialized_end=3904 + _globals['_GETLOCALDISKUSAGEREQUEST']._serialized_start=3906 + _globals['_GETLOCALDISKUSAGEREQUEST']._serialized_end=3932 + _globals['_GETLOCALDISKUSAGERESPONSE']._serialized_start=3934 + _globals['_GETLOCALDISKUSAGERESPONSE']._serialized_end=4002 + _globals['_LOCALCONTENTADDRESSABLESTORAGE']._serialized_start=4005 + _globals['_LOCALCONTENTADDRESSABLESTORAGE']._serialized_end=5341 # @@protoc_insertion_point(module_scope) diff --git a/src/buildstream/_protos/build/buildgrid/local_cas_pb2.pyi b/src/buildstream/_protos/build/buildgrid/local_cas_pb2.pyi index 0dcaf3fb3..86a3bf22a 100644 --- a/src/buildstream/_protos/build/buildgrid/local_cas_pb2.pyi +++ b/src/buildstream/_protos/build/buildgrid/local_cas_pb2.pyi @@ -1,13 +1,31 @@ from build.bazel.remote.execution.v2 import remote_execution_pb2 as _remote_execution_pb2 from google.rpc import status_pb2 as _status_pb2 from google.protobuf import duration_pb2 as _duration_pb2 +from google.protobuf import wrappers_pb2 as _wrappers_pb2 from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor +class GetLocalServerDetailsRequest(_message.Message): + __slots__ = ("instance_name",) + INSTANCE_NAME_FIELD_NUMBER: _ClassVar[int] + instance_name: str + def __init__(self, instance_name: _Optional[str] = ...) -> None: ... + +class LocalServerDetails(_message.Message): + __slots__ = ("hostname", "process_uid", "storage_root") + HOSTNAME_FIELD_NUMBER: _ClassVar[int] + PROCESS_UID_FIELD_NUMBER: _ClassVar[int] + STORAGE_ROOT_FIELD_NUMBER: _ClassVar[int] + hostname: str + process_uid: int + storage_root: str + def __init__(self, hostname: _Optional[str] = ..., process_uid: _Optional[int] = ..., storage_root: _Optional[str] = ...) -> None: ... + class FetchMissingBlobsRequest(_message.Message): __slots__ = ("instance_name", "blob_digests") INSTANCE_NAME_FIELD_NUMBER: _ClassVar[int] @@ -77,7 +95,15 @@ class UploadTreeResponse(_message.Message): def __init__(self) -> None: ... class StageTreeRequest(_message.Message): - __slots__ = ("instance_name", "root_digest", "path", "access_credentials") + __slots__ = ("instance_name", "root_digest", "path", "access_credentials", "remote_apis_socket_path", "pre_unstage_commands", "staging_mode") + class StagingMode(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + DEFAULT: _ClassVar[StageTreeRequest.StagingMode] + FUSE: _ClassVar[StageTreeRequest.StagingMode] + COPY_OR_LINK: _ClassVar[StageTreeRequest.StagingMode] + DEFAULT: StageTreeRequest.StagingMode + FUSE: StageTreeRequest.StagingMode + COPY_OR_LINK: StageTreeRequest.StagingMode class Credentials(_message.Message): __slots__ = ("uid", "gid") UID_FIELD_NUMBER: _ClassVar[int] @@ -89,11 +115,17 @@ class StageTreeRequest(_message.Message): ROOT_DIGEST_FIELD_NUMBER: _ClassVar[int] PATH_FIELD_NUMBER: _ClassVar[int] ACCESS_CREDENTIALS_FIELD_NUMBER: _ClassVar[int] + REMOTE_APIS_SOCKET_PATH_FIELD_NUMBER: _ClassVar[int] + PRE_UNSTAGE_COMMANDS_FIELD_NUMBER: _ClassVar[int] + STAGING_MODE_FIELD_NUMBER: _ClassVar[int] instance_name: str root_digest: _remote_execution_pb2.Digest path: str access_credentials: StageTreeRequest.Credentials - def __init__(self, instance_name: _Optional[str] = ..., root_digest: _Optional[_Union[_remote_execution_pb2.Digest, _Mapping]] = ..., path: _Optional[str] = ..., access_credentials: _Optional[_Union[StageTreeRequest.Credentials, _Mapping]] = ...) -> None: ... + remote_apis_socket_path: str + pre_unstage_commands: _containers.RepeatedScalarFieldContainer[str] + staging_mode: StageTreeRequest.StagingMode + def __init__(self, instance_name: _Optional[str] = ..., root_digest: _Optional[_Union[_remote_execution_pb2.Digest, _Mapping]] = ..., path: _Optional[str] = ..., access_credentials: _Optional[_Union[StageTreeRequest.Credentials, _Mapping]] = ..., remote_apis_socket_path: _Optional[str] = ..., pre_unstage_commands: _Optional[_Iterable[str]] = ..., staging_mode: _Optional[_Union[StageTreeRequest.StagingMode, str]] = ...) -> None: ... class StageTreeResponse(_message.Message): __slots__ = ("path",) @@ -102,7 +134,7 @@ class StageTreeResponse(_message.Message): def __init__(self, path: _Optional[str] = ...) -> None: ... class CaptureTreeRequest(_message.Message): - __slots__ = ("instance_name", "root", "path", "bypass_local_cache", "node_properties", "move_files", "output_directory_format", "skip_upload") + __slots__ = ("instance_name", "root", "path", "bypass_local_cache", "node_properties", "move_files", "output_directory_format", "skip_upload", "unix_mode_mask") INSTANCE_NAME_FIELD_NUMBER: _ClassVar[int] ROOT_FIELD_NUMBER: _ClassVar[int] PATH_FIELD_NUMBER: _ClassVar[int] @@ -111,6 +143,7 @@ class CaptureTreeRequest(_message.Message): MOVE_FILES_FIELD_NUMBER: _ClassVar[int] OUTPUT_DIRECTORY_FORMAT_FIELD_NUMBER: _ClassVar[int] SKIP_UPLOAD_FIELD_NUMBER: _ClassVar[int] + UNIX_MODE_MASK_FIELD_NUMBER: _ClassVar[int] instance_name: str root: str path: _containers.RepeatedScalarFieldContainer[str] @@ -119,7 +152,8 @@ class CaptureTreeRequest(_message.Message): move_files: bool output_directory_format: _remote_execution_pb2.Command.OutputDirectoryFormat skip_upload: bool - def __init__(self, instance_name: _Optional[str] = ..., root: _Optional[str] = ..., path: _Optional[_Iterable[str]] = ..., bypass_local_cache: bool = ..., node_properties: _Optional[_Iterable[str]] = ..., move_files: bool = ..., output_directory_format: _Optional[_Union[_remote_execution_pb2.Command.OutputDirectoryFormat, str]] = ..., skip_upload: bool = ...) -> None: ... + unix_mode_mask: _wrappers_pb2.UInt32Value + def __init__(self, instance_name: _Optional[str] = ..., root: _Optional[str] = ..., path: _Optional[_Iterable[str]] = ..., bypass_local_cache: bool = ..., node_properties: _Optional[_Iterable[str]] = ..., move_files: bool = ..., output_directory_format: _Optional[_Union[_remote_execution_pb2.Command.OutputDirectoryFormat, str]] = ..., skip_upload: bool = ..., unix_mode_mask: _Optional[_Union[_wrappers_pb2.UInt32Value, _Mapping]] = ...) -> None: ... class CaptureTreeResponse(_message.Message): __slots__ = ("responses",) @@ -139,7 +173,7 @@ class CaptureTreeResponse(_message.Message): def __init__(self, responses: _Optional[_Iterable[_Union[CaptureTreeResponse.Response, _Mapping]]] = ...) -> None: ... class CaptureFilesRequest(_message.Message): - __slots__ = ("instance_name", "root", "path", "bypass_local_cache", "node_properties", "move_files", "skip_upload") + __slots__ = ("instance_name", "root", "path", "bypass_local_cache", "node_properties", "move_files", "skip_upload", "unix_mode_mask") INSTANCE_NAME_FIELD_NUMBER: _ClassVar[int] ROOT_FIELD_NUMBER: _ClassVar[int] PATH_FIELD_NUMBER: _ClassVar[int] @@ -147,6 +181,7 @@ class CaptureFilesRequest(_message.Message): NODE_PROPERTIES_FIELD_NUMBER: _ClassVar[int] MOVE_FILES_FIELD_NUMBER: _ClassVar[int] SKIP_UPLOAD_FIELD_NUMBER: _ClassVar[int] + UNIX_MODE_MASK_FIELD_NUMBER: _ClassVar[int] instance_name: str root: str path: _containers.RepeatedScalarFieldContainer[str] @@ -154,7 +189,8 @@ class CaptureFilesRequest(_message.Message): node_properties: _containers.RepeatedScalarFieldContainer[str] move_files: bool skip_upload: bool - def __init__(self, instance_name: _Optional[str] = ..., root: _Optional[str] = ..., path: _Optional[_Iterable[str]] = ..., bypass_local_cache: bool = ..., node_properties: _Optional[_Iterable[str]] = ..., move_files: bool = ..., skip_upload: bool = ...) -> None: ... + unix_mode_mask: _wrappers_pb2.UInt32Value + def __init__(self, instance_name: _Optional[str] = ..., root: _Optional[str] = ..., path: _Optional[_Iterable[str]] = ..., bypass_local_cache: bool = ..., node_properties: _Optional[_Iterable[str]] = ..., move_files: bool = ..., skip_upload: bool = ..., unix_mode_mask: _Optional[_Union[_wrappers_pb2.UInt32Value, _Mapping]] = ...) -> None: ... class CaptureFilesResponse(_message.Message): __slots__ = ("responses",) @@ -196,7 +232,7 @@ class GetInstanceNameForRemoteResponse(_message.Message): def __init__(self, instance_name: _Optional[str] = ...) -> None: ... class Remote(_message.Message): - __slots__ = ("url", "instance_name", "server_cert", "client_key", "client_cert", "access_token_path", "access_token_reload_interval", "keepalive_time", "retry_limit", "retry_delay", "request_timeout") + __slots__ = ("url", "instance_name", "server_cert", "client_key", "client_cert", "access_token_path", "access_token_reload_interval", "keepalive_time", "retry_limit", "retry_delay", "request_timeout", "read_only") URL_FIELD_NUMBER: _ClassVar[int] INSTANCE_NAME_FIELD_NUMBER: _ClassVar[int] SERVER_CERT_FIELD_NUMBER: _ClassVar[int] @@ -208,6 +244,7 @@ class Remote(_message.Message): RETRY_LIMIT_FIELD_NUMBER: _ClassVar[int] RETRY_DELAY_FIELD_NUMBER: _ClassVar[int] REQUEST_TIMEOUT_FIELD_NUMBER: _ClassVar[int] + READ_ONLY_FIELD_NUMBER: _ClassVar[int] url: str instance_name: str server_cert: bytes @@ -219,7 +256,8 @@ class Remote(_message.Message): retry_limit: int retry_delay: _duration_pb2.Duration request_timeout: _duration_pb2.Duration - def __init__(self, url: _Optional[str] = ..., instance_name: _Optional[str] = ..., server_cert: _Optional[bytes] = ..., client_key: _Optional[bytes] = ..., client_cert: _Optional[bytes] = ..., access_token_path: _Optional[str] = ..., access_token_reload_interval: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., keepalive_time: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., retry_limit: _Optional[int] = ..., retry_delay: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., request_timeout: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ...) -> None: ... + read_only: bool + def __init__(self, url: _Optional[str] = ..., instance_name: _Optional[str] = ..., server_cert: _Optional[bytes] = ..., client_key: _Optional[bytes] = ..., client_cert: _Optional[bytes] = ..., access_token_path: _Optional[str] = ..., access_token_reload_interval: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., keepalive_time: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., retry_limit: _Optional[int] = ..., retry_delay: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., request_timeout: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., read_only: bool = ...) -> None: ... class GetInstanceNameForRemotesRequest(_message.Message): __slots__ = ("instance_name", "content_addressable_storage", "remote_asset", "action_cache", "execution") diff --git a/src/buildstream/_protos/build/buildgrid/local_cas_pb2_grpc.py b/src/buildstream/_protos/build/buildgrid/local_cas_pb2_grpc.py index f2ab82b2d..6c0a69e7e 100644 --- a/src/buildstream/_protos/build/buildgrid/local_cas_pb2_grpc.py +++ b/src/buildstream/_protos/build/buildgrid/local_cas_pb2_grpc.py @@ -34,6 +34,11 @@ def __init__(self, channel): Args: channel: A grpc.Channel. """ + self.GetLocalServerDetails = channel.unary_unary( + '/build.buildgrid.LocalContentAddressableStorage/GetLocalServerDetails', + request_serializer=build_dot_buildgrid_dot_local__cas__pb2.GetLocalServerDetailsRequest.SerializeToString, + response_deserializer=build_dot_buildgrid_dot_local__cas__pb2.LocalServerDetails.FromString, + _registered_method=True) self.FetchMissingBlobs = channel.unary_unary( '/build.buildgrid.LocalContentAddressableStorage/FetchMissingBlobs', request_serializer=build_dot_buildgrid_dot_local__cas__pb2.FetchMissingBlobsRequest.SerializeToString, @@ -94,6 +99,15 @@ def __init__(self, channel): class LocalContentAddressableStorageServicer(object): """Missing associated documentation comment in .proto file.""" + def GetLocalServerDetails(self, request, context): + """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. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def FetchMissingBlobs(self, request, context): """Fetch blobs from a remote CAS to the local cache. @@ -277,6 +291,11 @@ def GetLocalDiskUsage(self, request, context): def add_LocalContentAddressableStorageServicer_to_server(servicer, server): rpc_method_handlers = { + 'GetLocalServerDetails': grpc.unary_unary_rpc_method_handler( + servicer.GetLocalServerDetails, + request_deserializer=build_dot_buildgrid_dot_local__cas__pb2.GetLocalServerDetailsRequest.FromString, + response_serializer=build_dot_buildgrid_dot_local__cas__pb2.LocalServerDetails.SerializeToString, + ), 'FetchMissingBlobs': grpc.unary_unary_rpc_method_handler( servicer.FetchMissingBlobs, request_deserializer=build_dot_buildgrid_dot_local__cas__pb2.FetchMissingBlobsRequest.FromString, @@ -343,6 +362,33 @@ def add_LocalContentAddressableStorageServicer_to_server(servicer, server): class LocalContentAddressableStorage(object): """Missing associated documentation comment in .proto file.""" + @staticmethod + def GetLocalServerDetails(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/build.buildgrid.LocalContentAddressableStorage/GetLocalServerDetails', + build_dot_buildgrid_dot_local__cas__pb2.GetLocalServerDetailsRequest.SerializeToString, + build_dot_buildgrid_dot_local__cas__pb2.LocalServerDetails.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + @staticmethod def FetchMissingBlobs(request, target, From bdf0d46d242f41508a808917850fab2e373bc00f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Billeter?= Date: Fri, 11 Jul 2025 14:09:32 +0200 Subject: [PATCH 6/8] sandbox: Add helper method to fetch action result outputs --- src/buildstream/sandbox/_sandboxreapi.py | 27 +++++++++++++++++++++++ src/buildstream/sandbox/_sandboxremote.py | 20 +---------------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/buildstream/sandbox/_sandboxreapi.py b/src/buildstream/sandbox/_sandboxreapi.py index 262cb3f04..9b95f63fa 100644 --- a/src/buildstream/sandbox/_sandboxreapi.py +++ b/src/buildstream/sandbox/_sandboxreapi.py @@ -160,6 +160,33 @@ def _create_command(self, command, working_directory, environment, read_write_di platform=platform, ) + def _fetch_action_result_outputs(self, casremote, action_result): + # This also ensures that the outputs are uploaded to the cache + # storage-service, if configured + + context = self._get_context() + cascache = context.get_cascache() + + # Fetch outputs + for output_directory in action_result.output_directories: + # Now do a pull to ensure we have the full directory structure. + # We first try the root_directory_digest we requested, then fall back to tree_digest + + root_directory_digest = output_directory.root_directory_digest + if root_directory_digest and root_directory_digest.hash: + cascache.fetch_directory(casremote, root_directory_digest) + continue + + tree_digest = output_directory.tree_digest + if tree_digest and tree_digest.hash: + cascache.pull_tree(casremote, tree_digest) + continue + + raise SandboxError("Output directory structure had no digest attached.") + + # Fetch stdout and stderr blobs + cascache.fetch_blobs(casremote, [action_result.stdout_digest, action_result.stderr_digest]) + def _process_job_output(self, working_directory, output_directories, output_files, *, failure): # Reads the remote execution server response to an execution request. # diff --git a/src/buildstream/sandbox/_sandboxremote.py b/src/buildstream/sandbox/_sandboxremote.py index 7d40088e8..4cd1caaeb 100644 --- a/src/buildstream/sandbox/_sandboxremote.py +++ b/src/buildstream/sandbox/_sandboxremote.py @@ -205,25 +205,7 @@ def _execute_action(self, action, flags): operation = self.run_remote_command(action_digest) action_result = self._extract_action_result(operation) - # Fetch outputs - for output_directory in action_result.output_directories: - # Now do a pull to ensure we have the full directory structure. - # We first try the root_directory_digest we requested, then fall back to tree_digest - - root_directory_digest = output_directory.root_directory_digest - if root_directory_digest and root_directory_digest.hash: - cascache.fetch_directory(casremote, root_directory_digest) - continue - - tree_digest = output_directory.tree_digest - if tree_digest and tree_digest.hash: - cascache.pull_tree(casremote, tree_digest) - continue - - raise SandboxError("Output directory structure had no digest attached.") - - # Fetch stdout and stderr blobs - cascache.fetch_blobs(casremote, [action_result.stdout_digest, action_result.stderr_digest]) + self._fetch_action_result_outputs(casremote, action_result) # Forward remote stdout and stderr if stdout: From b8e6876d760641c2b6ccc3e2578eba48d0a33332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Billeter?= Date: Sat, 28 Jun 2025 14:23:43 +0200 Subject: [PATCH 7/8] Support remote action cache for nested execution --- doc/source/using_config.rst | 14 ++++++- src/buildstream/_frontend/widget.py | 3 +- src/buildstream/_remotespec.py | 35 ++++++++++++---- src/buildstream/element.py | 2 +- src/buildstream/sandbox/_reremote.py | 33 +++++++++++++++ .../sandbox/_sandboxbuildboxrun.py | 41 +++++++++++++++++-- src/buildstream/sandbox/_sandboxremote.py | 2 +- 7 files changed, 116 insertions(+), 14 deletions(-) diff --git a/doc/source/using_config.rst b/doc/source/using_config.rst index d6d75353d..8911e3f70 100644 --- a/doc/source/using_config.rst +++ b/doc/source/using_config.rst @@ -1117,7 +1117,8 @@ are the lowest priority configuration. Remote execution ~~~~~~~~~~~~~~~~ BuildStream supports building remotely using the -`Google Remote Execution API (REAPI). `_. +`Google Remote Execution API (REAPI) `_. +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: @@ -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 ` specifying @@ -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 `. + .. _user_config_remote_execution_service: diff --git a/src/buildstream/_frontend/widget.py b/src/buildstream/_frontend/widget.py index 09b824ff3..e061f0c2f 100644 --- a/src/buildstream/_frontend/widget.py +++ b/src/buildstream/_frontend/widget.py @@ -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: diff --git a/src/buildstream/_remotespec.py b/src/buildstream/_remotespec.py index 1a22ee043..f0ffd4d3e 100644 --- a/src/buildstream/_remotespec.py +++ b/src/buildstream/_remotespec.py @@ -239,6 +239,7 @@ def to_localcas_remote(self, remote): # spec_node: The configuration node describing the spec. # basedir: The base directory from which to find certificates. # remote_execution: Whether this spec is used for remote execution (some keys are invalid) + # action_cache: Whether this spec is used for remote execution action cache # # Returns: # The described RemoteSpec instance. @@ -248,7 +249,12 @@ def to_localcas_remote(self, remote): # @classmethod def new_from_node( - cls, spec_node: MappingNode, basedir: Optional[str] = None, *, remote_execution: bool = False + cls, + spec_node: MappingNode, + basedir: Optional[str] = None, + *, + remote_execution: bool = False, + action_cache: bool = False, ) -> "RemoteSpec": server_cert: Optional[str] = None client_key: Optional[str] = None @@ -261,8 +267,10 @@ def new_from_node( valid_keys: List[str] = ["url", "instance-name", "auth", "connection-config"] if not remote_execution: remote_type = cast(str, spec_node.get_enum("type", RemoteType, default=RemoteType.ALL)) + valid_keys += ["type"] + if not remote_execution or action_cache: push = spec_node.get_bool("push", default=False) - valid_keys += ["push", "type"] + valid_keys += ["push"] spec_node.validate_keys(valid_keys) @@ -500,9 +508,9 @@ def maybe_read_file(filename: Optional[str]) -> Optional[bytes]: # class RemoteExecutionSpec: def __init__( - self, exec_spec: RemoteSpec, storage_spec: Optional[RemoteSpec], action_spec: Optional[RemoteSpec] + self, exec_spec: Optional[RemoteSpec], storage_spec: Optional[RemoteSpec], action_spec: Optional[RemoteSpec] ) -> None: - self.exec_spec: RemoteSpec = exec_spec + self.exec_spec: Optional[RemoteSpec] = exec_spec self.storage_spec: Optional[RemoteSpec] = storage_spec self.action_spec: Optional[RemoteSpec] = action_spec @@ -526,7 +534,7 @@ def new_from_node( ) -> "RemoteExecutionSpec": node.validate_keys(["execution-service", "storage-service", "action-cache-service"]) - exec_node = node.get_mapping("execution-service") + exec_node = node.get_mapping("execution-service", default=None) storage_node = node.get_mapping("storage-service", default=None) if not storage_node and not remote_cache: provenance = node.get_provenance() @@ -538,7 +546,20 @@ def new_from_node( ) action_node = node.get_mapping("action-cache-service", default=None) - exec_spec = RemoteSpec.new_from_node(exec_node, basedir, remote_execution=True) + if not exec_node and not action_node: + provenance = node.get_provenance() + raise LoadError( + "{}: At least one of `execution-service` or `action-cache-service` need to be specified in the 'remote-execution' section".format( + provenance + ), + LoadErrorReason.INVALID_DATA, + ) + + exec_spec: Optional[RemoteSpec] + if exec_node: + exec_spec = RemoteSpec.new_from_node(exec_node, basedir, remote_execution=True) + else: + exec_spec = None storage_spec: Optional[RemoteSpec] if storage_node: @@ -548,7 +569,7 @@ def new_from_node( action_spec: Optional[RemoteSpec] if action_node: - action_spec = RemoteSpec.new_from_node(action_node, basedir, remote_execution=True) + action_spec = RemoteSpec.new_from_node(action_node, basedir, remote_execution=True, action_cache=True) else: action_spec = None diff --git a/src/buildstream/element.py b/src/buildstream/element.py index ca8080314..f02f45f1a 100644 --- a/src/buildstream/element.py +++ b/src/buildstream/element.py @@ -2822,7 +2822,7 @@ def __sandbox(self, stdout=None, stderr=None, config=None, allow_remote=True): else: output_node_properties = None - if allow_remote and context.remote_execution_specs: + if allow_remote and context.remote_execution_specs and context.remote_execution_specs.exec_spec: with SandboxRemote( context, project, diff --git a/src/buildstream/sandbox/_reremote.py b/src/buildstream/sandbox/_reremote.py index ca320ae7f..79853fa34 100644 --- a/src/buildstream/sandbox/_reremote.py +++ b/src/buildstream/sandbox/_reremote.py @@ -12,9 +12,13 @@ # limitations under the License. # +import grpc + +from .._protos.build.bazel.remote.execution.v2 import remote_execution_pb2 from .._protos.build.buildgrid import local_cas_pb2 from .._cas import CASRemote +from .._exceptions import RemoteError class RERemote(CASRemote): @@ -33,10 +37,16 @@ def _configure_protocols(self): self.remote_execution_specs.storage_spec.to_localcas_remote(request.content_addressable_storage) else: self.spec.to_localcas_remote(request.content_addressable_storage) + request.content_addressable_storage.read_only = True if self.remote_execution_specs.exec_spec: self.remote_execution_specs.exec_spec.to_localcas_remote(request.execution) + request.content_addressable_storage.read_only = False if self.remote_execution_specs.action_spec: self.remote_execution_specs.action_spec.to_localcas_remote(request.action_cache) + if self.remote_execution_specs.action_spec.push: + request.content_addressable_storage.read_only = False + else: + request.action_cache.read_only = True response = local_cas.GetInstanceNameForRemotes(request) self.local_cas_instance_name = response.instance_name @@ -44,3 +54,26 @@ def _configure_protocols(self): self.exec_service = casd.get_exec_service() self.operations_service = casd.get_operations_service() self.ac_service = casd.get_ac_service() + + def _check(self): + super()._check() + + if not self.remote_execution_specs.exec_spec: + request = remote_execution_pb2.ExecuteRequest() + request.instance_name = self.local_cas_instance_name + + try: + for _ in self.exec_service.Execute(request): + pass + except grpc.RpcError as e: + if e.code() == grpc.StatusCode.INVALID_ARGUMENT: + # Expected error as the request doesn't specify an action digest. + pass + elif e.code() == grpc.StatusCode.UNIMPLEMENTED: + raise RemoteError( + "buildbox-casd >= 1.3.23 is required to support local execution with a remote action cache" + ) + else: + raise RemoteError( + "Unexpected error in remote cache initialization {}: {}".format(e.code().name, e.details()) + ) diff --git a/src/buildstream/sandbox/_sandboxbuildboxrun.py b/src/buildstream/sandbox/_sandboxbuildboxrun.py index d79f54b2f..99dadad4b 100644 --- a/src/buildstream/sandbox/_sandboxbuildboxrun.py +++ b/src/buildstream/sandbox/_sandboxbuildboxrun.py @@ -17,6 +17,7 @@ import sys from contextlib import ExitStack +import grpc import psutil from .. import utils, _signals @@ -24,6 +25,7 @@ from .._exceptions import SandboxError, SandboxUnavailableError from .._platform import Platform from .._protos.build.bazel.remote.execution.v2 import remote_execution_pb2 +from ._reremote import RERemote from ._sandboxreapi import SandboxREAPI @@ -32,6 +34,27 @@ # BuildBox-based sandbox implementation. # class SandboxBuildBoxRun(SandboxREAPI): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + context = self._get_context() + cascache = context.get_cascache() + + re_specs = context.remote_execution_specs + if re_specs and re_specs.action_spec: + self.re_remote = RERemote(context.remote_cache_spec, re_specs, cascache) + try: + self.re_remote.init() + self.re_remote.check() + except grpc.RpcError as e: + urls = set() + if re_specs.storage_spec: + urls.add(re_specs.storage_spec.url) + urls.add(re_specs.action_spec.url) + raise SandboxError("Failed to contact remote cache endpoint at {}: {}".format(sorted(urls), e)) from e + else: + self.re_remote = None + @classmethod def __buildbox_run(cls): return utils._get_host_tool_internal("buildbox-run", search_subprojects_dir="buildbox") @@ -91,8 +114,10 @@ def _execute_action(self, action, flags): casd = cascache.get_casd() config = self._get_config() - if config.remote_apis_socket_path and context.remote_cache_spec: - raise SandboxError("'remote-apis-socket' is not currently supported with 'storage-service'.") + if config.remote_apis_socket_path and context.remote_cache_spec and not self.re_remote: + raise SandboxError( + "Using 'remote-apis-socket' with 'storage-service' requires 'action-cache-service' or 'execution-service' configured in the 'remote-execution' section." + ) with utils._tempnamedfile() as action_file, utils._tempnamedfile() as result_file: action_file.write(action.SerializeToString()) @@ -105,6 +130,9 @@ def _execute_action(self, action, flags): "--action-result={}".format(result_file.name), ] + if self.re_remote: + buildbox_command.append("--instance={}".format(self.re_remote.local_cas_instance_name)) + # Do not redirect stdout/stderr if "no-logs-capture" in self._capabilities: buildbox_command.append("--no-logs-capture") @@ -145,7 +173,14 @@ def _execute_action(self, action, flags): interactive=(flags & _SandboxFlags.INTERACTIVE), ) - return remote_execution_pb2.ActionResult().FromString(result_file.read()) + action_result = remote_execution_pb2.ActionResult().FromString(result_file.read()) + + if self.re_remote and context.remote_execution_specs.storage_spec and context.remote_cache_spec: + # This ensures that the outputs are uploaded to the cache storage-service + # in case different CAS remotes have been configured in the `cache` and `remote-execution` sections. + self._fetch_action_result_outputs(self.re_remote, action_result) + + return action_result def _run_buildbox(self, argv, stdin, stdout, stderr, *, interactive): def kill_proc(): diff --git a/src/buildstream/sandbox/_sandboxremote.py b/src/buildstream/sandbox/_sandboxremote.py index 4cd1caaeb..b087e053f 100644 --- a/src/buildstream/sandbox/_sandboxremote.py +++ b/src/buildstream/sandbox/_sandboxremote.py @@ -40,7 +40,7 @@ def __init__(self, *args, **kwargs): cascache = context.get_cascache() specs = context.remote_execution_specs - if specs is None: + if specs is None or specs.exec_spec is None: return self.storage_spec = specs.storage_spec From be2004432234a66229e4bd515c50abc0c6948783 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Billeter?= Date: Fri, 4 Jul 2025 09:25:56 +0200 Subject: [PATCH 8/8] tests/integration/sandbox.py: Add test for remote action cache config --- tests/integration/sandbox.py | 71 ++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/tests/integration/sandbox.py b/tests/integration/sandbox.py index 7d2d00123..c23ce8a55 100644 --- a/tests/integration/sandbox.py +++ b/tests/integration/sandbox.py @@ -21,6 +21,7 @@ from buildstream._testing import cli_integration as cli # pylint: disable=unused-import from buildstream._testing._utils.site import HAVE_SANDBOX +from tests.testutils import create_artifact_share pytestmark = pytest.mark.integration @@ -59,3 +60,73 @@ def test_remote_apis_socket(cli, datafiles): result = cli.run(project=project, args=["build", element_name]) assert result.exit_code == 0 + + +# Test configuration with remote action cache for nested REAPI. +@pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") +@pytest.mark.datafiles(DATA_DIR) +def test_remote_apis_socket_with_action_cache(cli, tmpdir, datafiles): + project = str(datafiles) + element_name = "sandbox/remote-apis-socket.bst" + + with create_artifact_share(os.path.join(str(tmpdir), "remote")) as share: + cli.configure( + { + "remote-execution": { + "storage-service": {"url": share.repo}, + "action-cache-service": {"url": share.repo, "push": True}, + } + } + ) + + result = cli.run(project=project, args=["build", element_name]) + assert result.exit_code == 0 + + +# Test configuration with cache storage-service and remote action cache for nested REAPI. +@pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") +@pytest.mark.datafiles(DATA_DIR) +def test_remote_apis_socket_with_cache_storage_service_and_action_cache(cli, tmpdir, datafiles): + project = str(datafiles) + element_name = "sandbox/remote-apis-socket.bst" + + with create_artifact_share(os.path.join(str(tmpdir), "remote")) as share: + cli.configure( + { + "cache": { + "storage-service": {"url": share.repo}, + }, + "remote-execution": { + "action-cache-service": {"url": share.repo, "push": True}, + }, + } + ) + + result = cli.run(project=project, args=["build", element_name]) + assert result.exit_code == 0 + + +# Test configuration with two different storage-services and remote action cache for nested REAPI. +@pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") +@pytest.mark.datafiles(DATA_DIR) +def test_remote_apis_socket_with_two_storage_services_and_action_cache(cli, tmpdir, datafiles): + project = str(datafiles) + element_name = "sandbox/remote-apis-socket.bst" + + with create_artifact_share(os.path.join(str(tmpdir), "remote1")) as share1, create_artifact_share( + os.path.join(str(tmpdir), "remote2") + ) as share2: + cli.configure( + { + "cache": { + "storage-service": {"url": share1.repo}, + }, + "remote-execution": { + "storage-service": {"url": share2.repo}, + "action-cache-service": {"url": share2.repo, "push": True}, + }, + } + ) + + result = cli.run(project=project, args=["build", element_name]) + assert result.exit_code == 0