Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • coverity
  • master
  • sminskyprimu/blake3
  • sotk/consolidate-leases-and-jobs/p2-write-only-leases
  • sotk/consolidate-leases-and-jobs/p3-drop-leases-table
  • sotk/features/lease-audit-table
  • sotk/logstream-testing
  • zchen723/skip-scheduler-metrics
  • 0.0.10
  • 0.0.11
  • 0.0.12
  • 0.0.13
  • 0.0.14
  • 0.0.16
  • 0.0.17
  • 0.0.19
  • 0.0.2
  • 0.0.20
  • 0.0.21
  • 0.0.23
  • 0.0.25
  • 0.0.26
  • 0.0.27
  • 0.0.28
  • 0.0.29
  • 0.0.3
  • 0.0.30
  • 0.0.31
  • 0.0.32
  • 0.0.33
  • 0.0.34
  • 0.0.35
  • 0.0.36
  • 0.0.37
  • 0.0.38
  • 0.0.39
  • 0.0.4
  • 0.0.40
  • 0.0.41
  • 0.0.42
  • 0.0.43
  • 0.0.44
  • 0.0.45
  • 0.0.46
  • 0.0.47
  • 0.0.48
  • 0.0.49
  • 0.0.5
  • 0.0.50
  • 0.0.51
  • 0.0.52
  • 0.0.53
  • 0.0.54
  • 0.0.55
  • 0.0.56
  • 0.0.57
  • 0.0.58
  • 0.0.59
  • 0.0.6
  • 0.0.60
  • 0.0.61
  • 0.0.62
  • 0.0.63
  • 0.0.64
  • 0.0.65
  • 0.0.66
  • 0.0.67
  • 0.0.68
  • 0.0.69
  • 0.0.7
  • 0.0.70
  • 0.0.71
  • 0.0.72
  • 0.0.73
  • 0.0.74
  • 0.0.75
  • 0.0.76
  • 0.0.78
  • 0.0.79
  • 0.0.8
  • 0.0.80
  • 0.0.81
  • 0.0.82
  • 0.0.83
  • 0.0.84
  • 0.0.85
  • 0.0.86
  • 0.0.87
  • 0.0.88
  • 0.0.89
  • 0.0.9
  • 0.0.90
  • 0.0.91
  • 0.0.92
  • 0.0.93
  • 0.0.94
  • 0.0.95
  • 0.0.96
  • 0.0.97
  • 0.0.98
  • 0.1.0
  • 0.1.1
  • 0.1.10
  • 0.1.11
  • 0.1.12
  • 0.1.13
  • 0.1.14
  • 0.1.15
108 results

Target

Select target project
  • edbaunton/buildgrid
  • BuildGrid/buildgrid
  • bloomberg/buildgrid
  • devcurmudgeon/buildgrid
  • mhadjimichael/buildgrid
  • jmacarthur/buildgrid
  • rkothur/buildgrid
  • valentindavid/buildgrid
  • jjardon/buildgrid
  • RichKen/buildgrid
  • jbonney/buildgrid
  • onsha_alexander/buildgrid
  • santigl/buildgrid
  • mostynb/buildgrid
  • hoffbrinkle/buildgrid
  • Malinskiy/buildgrid
  • coldtom/buildgrid
  • azeemb_a/buildgrid
  • pointswaves/buildgrid
  • BenjaminSchubert/buildgrid
  • michaellee8/buildgrid
  • anil-anil/buildgrid
  • seanborg/buildgrid
  • jdelong12/buildgrid
  • jclay/buildgrid
  • bweston92/buildgrid
  • zchen723/buildgrid
  • cpratt34/buildgrid
  • armbiant/apache-buildgrid
  • armbiant/android-buildgrid
  • itsme300/buildgrid
  • sbairoliya/buildgrid
32 results
Select Git revision
  • coverity
  • master
  • sminskyprimu/blake3
  • sotk/consolidate-leases-and-jobs/p2-write-only-leases
  • sotk/consolidate-leases-and-jobs/p3-drop-leases-table
  • sotk/features/lease-audit-table
  • sotk/logstream-testing
  • zchen723/skip-scheduler-metrics
  • 0.0.10
  • 0.0.11
  • 0.0.12
  • 0.0.13
  • 0.0.14
  • 0.0.16
  • 0.0.17
  • 0.0.19
  • 0.0.2
  • 0.0.20
  • 0.0.21
  • 0.0.23
  • 0.0.25
  • 0.0.26
  • 0.0.27
  • 0.0.28
  • 0.0.29
  • 0.0.3
  • 0.0.30
  • 0.0.31
  • 0.0.32
  • 0.0.33
  • 0.0.34
  • 0.0.35
  • 0.0.36
  • 0.0.37
  • 0.0.38
  • 0.0.39
  • 0.0.4
  • 0.0.40
  • 0.0.41
  • 0.0.42
  • 0.0.43
  • 0.0.44
  • 0.0.45
  • 0.0.46
  • 0.0.47
  • 0.0.48
  • 0.0.49
  • 0.0.5
  • 0.0.50
  • 0.0.51
  • 0.0.52
  • 0.0.53
  • 0.0.54
  • 0.0.55
  • 0.0.56
  • 0.0.57
  • 0.0.58
  • 0.0.59
  • 0.0.6
  • 0.0.60
  • 0.0.61
  • 0.0.62
  • 0.0.63
  • 0.0.64
  • 0.0.65
  • 0.0.66
  • 0.0.67
  • 0.0.68
  • 0.0.69
  • 0.0.7
  • 0.0.70
  • 0.0.71
  • 0.0.72
  • 0.0.73
  • 0.0.74
  • 0.0.75
  • 0.0.76
  • 0.0.78
  • 0.0.79
  • 0.0.8
  • 0.0.80
  • 0.0.81
  • 0.0.82
  • 0.0.83
  • 0.0.84
  • 0.0.85
  • 0.0.86
  • 0.0.87
  • 0.0.88
  • 0.0.89
  • 0.0.9
  • 0.0.90
  • 0.0.91
  • 0.0.92
  • 0.0.93
  • 0.0.94
  • 0.0.95
  • 0.0.96
  • 0.0.97
  • 0.0.98
  • 0.1.0
  • 0.1.1
  • 0.1.10
  • 0.1.11
  • 0.1.12
  • 0.1.13
  • 0.1.14
  • 0.1.15
108 results
Show changes
Commits on Source (4)
Showing
with 573 additions and 274 deletions
......@@ -30,7 +30,7 @@ before_script:
.run-dummy-job-template: &dummy-job
stage: test
script:
- ${BGD} server start --allow-insecure &
- ${BGD} server start buildgrid/_app/settings/default.yml &
- sleep 1 # Allow server to boot
- ${BGD} bot dummy &
- ${BGD} execute request-dummy --wait-for-completion
......
......@@ -170,11 +170,8 @@ class BuildGridCLI(click.MultiCommand):
return commands
def get_command(self, context, name):
try:
mod = __import__(name='buildgrid._app.commands.cmd_{}'.format(name),
fromlist=['cli'])
except ImportError:
return None
mod = __import__(name='buildgrid._app.commands.cmd_{}'.format(name),
fromlist=['cli'])
return mod.cli
......
......@@ -33,7 +33,6 @@ import grpc
from buildgrid.utils import merkle_maker, create_digest, write_fetch_blob
from buildgrid._protos.build.bazel.remote.execution.v2 import remote_execution_pb2, remote_execution_pb2_grpc
from buildgrid._protos.google.bytestream import bytestream_pb2_grpc
from buildgrid._protos.google.longrunning import operations_pb2, operations_pb2_grpc
from ..cli import pass_context
......@@ -62,7 +61,7 @@ def cli(context, remote, instance_name, client_key, client_cert, server_cert):
credentials = context.load_client_credentials(client_key, client_cert, server_cert)
if not credentials:
click.echo("ERROR: no TLS keys were specified and no defaults could be found.\n" +
"Use --allow-insecure in order to deactivate TLS encryption.\n", err=True)
"Use `insecure-mode: false` in order to deactivate TLS encryption.\n", err=True)
sys.exit(-1)
context.channel = grpc.secure_channel(context.remote, credentials)
......@@ -99,37 +98,6 @@ def request_dummy(context, number, wait_for_completion):
context.logger.info(next(response))
@cli.command('status', short_help="Get the status of an operation.")
@click.argument('operation-name', nargs=1, type=click.STRING, required=True)
@pass_context
def operation_status(context, operation_name):
context.logger.info("Getting operation status...")
stub = operations_pb2_grpc.OperationsStub(context.channel)
request = operations_pb2.GetOperationRequest(name=operation_name)
response = stub.GetOperation(request)
context.logger.info(response)
@cli.command('list', short_help="List operations.")
@pass_context
def list_operations(context):
context.logger.info("Getting list of operations")
stub = operations_pb2_grpc.OperationsStub(context.channel)
request = operations_pb2.ListOperationsRequest(name=context.instance_name)
response = stub.ListOperations(request)
if not response.operations:
context.logger.warning("No operations to list")
return
for op in response.operations:
context.logger.info(op)
@cli.command('wait', short_help="Streams an operation until it is complete.")
@click.argument('operation-name', nargs=1, type=click.STRING, required=True)
@pass_context
......
# Copyright (C) 2018 Bloomberg LP
#
# 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.
"""
Operations command
=================
Check the status of operations
"""
import logging
from urllib.parse import urlparse
import sys
import click
import grpc
from buildgrid._protos.google.longrunning import operations_pb2, operations_pb2_grpc
from ..cli import pass_context
@click.group(name='operation', short_help="Long running operations commands")
@click.option('--remote', type=click.STRING, default='http://localhost:50051', show_default=True,
help="Remote execution server's URL (port defaults to 50051 if no specified).")
@click.option('--client-key', type=click.Path(exists=True, dir_okay=False), default=None,
help="Private client key for TLS (PEM-encoded)")
@click.option('--client-cert', type=click.Path(exists=True, dir_okay=False), default=None,
help="Public client certificate for TLS (PEM-encoded)")
@click.option('--server-cert', type=click.Path(exists=True, dir_okay=False), default=None,
help="Public server certificate for TLS (PEM-encoded)")
@click.option('--instance-name', type=click.STRING, default='main', show_default=True,
help="Targeted farm instance name.")
@pass_context
def cli(context, remote, instance_name, client_key, client_cert, server_cert):
url = urlparse(remote)
context.remote = '{}:{}'.format(url.hostname, url.port or 50051)
context.instance_name = instance_name
if url.scheme == 'http':
context.channel = grpc.insecure_channel(context.remote)
else:
credentials = context.load_client_credentials(client_key, client_cert, server_cert)
if not credentials:
click.echo("ERROR: no TLS keys were specified and no defaults could be found.\n" +
"Use `insecure-mode: false` in order to deactivate TLS encryption.\n", err=True)
sys.exit(-1)
context.channel = grpc.secure_channel(context.remote, credentials)
context.logger = logging.getLogger(__name__)
context.logger.debug("Starting for remote {}".format(context.remote))
@cli.command('status', short_help="Get the status of an operation.")
@click.argument('operation-name', nargs=1, type=click.STRING, required=True)
@pass_context
def status(context, operation_name):
context.logger.info("Getting operation status...")
stub = operations_pb2_grpc.OperationsStub(context.channel)
request = operations_pb2.GetOperationRequest(name=operation_name)
response = stub.GetOperation(request)
context.logger.info(response)
@cli.command('list', short_help="List operations.")
@pass_context
def lists(context):
context.logger.info("Getting list of operations")
stub = operations_pb2_grpc.OperationsStub(context.channel)
request = operations_pb2.ListOperationsRequest(name=context.instance_name)
response = stub.ListOperations(request)
if not response.operations:
context.logger.warning("No operations to list")
return
for op in response.operations:
context.logger.info(op)
......@@ -26,16 +26,14 @@ import sys
import click
from buildgrid.server import buildgrid_server
from buildgrid.server.cas.storage.disk import DiskStorage
from buildgrid.server.cas.storage.lru_memory_cache import LRUMemoryCache
from buildgrid.server.cas.storage.s3 import S3Storage
from buildgrid.server.cas.storage.with_cache import WithCacheStorage
from buildgrid.server.controller import ExecutionController
from buildgrid.server.actioncache.storage import ActionCache
from buildgrid.server.cas.instance import ByteStreamInstance, ContentAddressableStorageInstance
from buildgrid.server.referencestorage.storage import ReferenceCache
from ..cli import pass_context
_SIZE_PREFIXES = {'k': 2 ** 10, 'm': 2 ** 20, 'g': 2 ** 30, 't': 2 ** 40}
from ..settings import parser
from ..server import BuildGridServer
@click.group(name='server', short_help="Start a local server instance.")
......@@ -45,71 +43,58 @@ def cli(context):
@cli.command('start', short_help="Setup a new server instance.")
@click.argument('instances', nargs=-1, type=click.STRING)
@click.option('--port', type=click.INT, default='50051', show_default=True,
help="The port number to be listened.")
@click.option('--server-key', type=click.Path(exists=True, dir_okay=False), default=None,
help="Private server key for TLS (PEM-encoded)")
@click.option('--server-cert', type=click.Path(exists=True, dir_okay=False), default=None,
help="Public server certificate for TLS (PEM-encoded)")
@click.option('--client-certs', type=click.Path(exists=True, dir_okay=False), default=None,
help="Public client certificates for TLS (PEM-encoded, one single file)")
@click.option('--allow-insecure', type=click.BOOL, is_flag=True,
help="Whether or not to allow unencrypted connections.")
@click.option('--allow-update-action-result/--forbid-update-action-result',
'allow_uar', default=True, show_default=True,
help="Whether or not to allow clients to manually edit the action cache.")
@click.option('--max-cached-actions', type=click.INT, default=50, show_default=True,
help="Maximum number of actions to keep in the ActionCache.")
@click.option('--cas', type=click.Choice(('lru', 's3', 'disk', 'with-cache')),
help="The CAS storage type to use.")
@click.option('--cas-cache', type=click.Choice(('lru', 's3', 'disk')),
help="For --cas=with-cache, the CAS storage to use as the cache.")
@click.option('--cas-fallback', type=click.Choice(('lru', 's3', 'disk')),
help="For --cas=with-cache, the CAS storage to use as the fallback.")
@click.option('--cas-lru-size', type=click.STRING,
help="For --cas=lru, the LRU cache's memory limit.")
@click.option('--cas-s3-bucket', type=click.STRING,
help="For --cas=s3, the bucket name.")
@click.option('--cas-s3-endpoint', type=click.STRING,
help="For --cas=s3, the endpoint URI.")
@click.option('--cas-disk-directory', type=click.Path(file_okay=False, dir_okay=True, writable=True),
help="For --cas=disk, the folder to store CAS blobs in.")
@click.argument('CONFIG', type=click.Path(file_okay=True, dir_okay=False, writable=False))
@pass_context
def start(context, port, allow_insecure, server_key, server_cert, client_certs,
instances, max_cached_actions, allow_uar, cas, **cas_args):
"""Setups a new server instance."""
credentials = None
if not allow_insecure:
credentials = context.load_server_credentials(server_key, server_cert, client_certs)
if not credentials and not allow_insecure:
click.echo("ERROR: no TLS keys were specified and no defaults could be found.\n" +
"Use --allow-insecure in order to deactivate TLS encryption.\n", err=True)
sys.exit(-1)
context.credentials = credentials
context.port = port
context.logger.info("BuildGrid server booting up")
context.logger.info("Starting on port {}".format(port))
def start(context, config):
with open(config) as f:
settings = parser.get_parser().safe_load(f)
cas_storage = _make_cas_storage(context, cas, cas_args)
server_settings = settings['server']
insecure_mode = server_settings['insecure-mode']
if cas_storage is None:
context.logger.info("Running without CAS - action cache will be unavailable")
action_cache = None
else:
action_cache = ActionCache(cas_storage, max_cached_actions, allow_uar)
if instances is None:
instances = ['main']
credentials = None
if not insecure_mode:
server_key = server_settings['tls-server-key']
server_cert = server_settings['tls-server-cert']
client_certs = server_settings['tls-client-certs']
credentials = context.load_server_credentials(server_key, server_cert, client_certs)
server = buildgrid_server.BuildGridServer(port=context.port,
credentials=context.credentials,
instances=instances,
cas_storage=cas_storage,
action_cache=action_cache)
if not credentials:
click.echo("ERROR: no TLS keys were specified and no defaults could be found.\n" +
"Use --allow-insecure in order to deactivate TLS encryption.\n", err=True)
sys.exit(-1)
instances = settings['instances']
execution_controllers = _instance_maker(instances, ExecutionController)
execution_instances = {}
bots_interfaces = {}
operations_instances = {}
# TODO: map properly in parser
for k, v in execution_controllers.items():
execution_instances[k] = v.execution_instance
bots_interfaces[k] = v.bots_interface
operations_instances[k] = v.operations_instance
reference_caches = _instance_maker(instances, ReferenceCache)
action_caches = _instance_maker(instances, ActionCache)
cas = _instance_maker(instances, ContentAddressableStorageInstance)
bytestreams = _instance_maker(instances, ByteStreamInstance)
port = server_settings['port']
server = BuildGridServer(port=port,
credentials=credentials,
execution_instances=execution_instances,
bots_interfaces=bots_interfaces,
operations_instances=operations_instances,
reference_storage_instances=reference_caches,
action_cache_instances=action_caches,
cas_instances=cas,
bytestream_instances=bytestreams)
context.logger.info("Starting server on port {}".format(port))
loop = asyncio.get_event_loop()
try:
server.start()
......@@ -119,57 +104,20 @@ def start(context, port, allow_insecure, server_key, server_cert, client_certs,
pass
finally:
context.logger.info("Stopping server")
server.stop()
loop.close()
def _make_cas_storage(context, cas_type, cas_args):
"""Returns the storage provider corresponding to the given `cas_type`,
or None if the provider cannot be created.
"""
if cas_type == "lru":
if cas_args["cas_lru_size"] is None:
context.logger.error("--cas-lru-size is required for LRU CAS")
return None
try:
size = _parse_size(cas_args["cas_lru_size"])
except ValueError:
context.logger.error('Invalid LRU size "{0}"'.format(cas_args["cas_lru_size"]))
return None
return LRUMemoryCache(size)
elif cas_type == "s3":
if cas_args["cas_s3_bucket"] is None:
context.logger.error("--cas-s3-bucket is required for S3 CAS")
return None
if cas_args["cas_s3_endpoint"] is not None:
return S3Storage(cas_args["cas_s3_bucket"],
endpoint_url=cas_args["cas_s3_endpoint"])
return S3Storage(cas_args["cas_s3_bucket"])
elif cas_type == "disk":
if cas_args["cas_disk_directory"] is None:
context.logger.error("--cas-disk-directory is required for disk CAS")
return None
return DiskStorage(cas_args["cas_disk_directory"])
elif cas_type == "with-cache":
cache = _make_cas_storage(context, cas_args["cas_cache"], cas_args)
fallback = _make_cas_storage(context, cas_args["cas_fallback"], cas_args)
if cache is None:
context.logger.error("Missing cache provider for --cas=with-cache")
return None
elif fallback is None:
context.logger.error("Missing fallback provider for --cas=with-cache")
return None
return WithCacheStorage(cache, fallback)
elif cas_type is None:
return None
return None
def _parse_size(size):
"""Convert a string containing a size in bytes (e.g. '2GB') to a number."""
size = size.lower()
if size[-1] == 'b':
size = size[:-1]
if size[-1] in _SIZE_PREFIXES:
return int(size[:-1]) * _SIZE_PREFIXES[size[-1]]
return int(size)
# Turn away now if you want to keep your eyes
def _instance_maker(instances, service_type):
# TODO get this mapped in parser
made = {}
for instance in instances:
services = instance['services']
instance_name = instance['name']
for service in services:
if isinstance(service, service_type):
made[instance_name] = service
return made
# Copyright (C) 2018 Bloomberg LP
#
# 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.
"""
BuildGridServer
==============
Creates a BuildGrid server, binding all the requisite service instances together.
"""
import logging
from concurrent import futures
import grpc
from buildgrid.server.cas.service import ByteStreamService, ContentAddressableStorageService
from buildgrid.server.actioncache.service import ActionCacheService
from buildgrid.server.execution.service import ExecutionService
from buildgrid.server.operations.service import OperationsService
from buildgrid.server.bots.service import BotsService
from buildgrid.server.referencestorage.service import ReferenceStorageService
class BuildGridServer:
def __init__(self, port=50051, max_workers=10, credentials=None,
execution_instances=None, bots_interfaces=None, operations_instances=None,
operations_service_instances=None, reference_storage_instances=None,
action_cache_instances=None, cas_instances=None, bytestream_instances=None):
self.logger = logging.getLogger(__name__)
address = '[::]:{0}'.format(port)
server = grpc.server(futures.ThreadPoolExecutor(max_workers))
if credentials is not None:
self.logger.info("Secure connection")
server.add_secure_port(address, credentials)
else:
self.logger.info("Insecure connection")
server.add_insecure_port(address)
if execution_instances:
self.logger.debug("Adding execution instances {}".format(
execution_instances.keys()))
ExecutionService(server, execution_instances)
if bots_interfaces:
self.logger.debug("Adding bots interfaces {}".format(
bots_interfaces.keys()))
BotsService(server, bots_interfaces)
if operations_instances:
self.logger.debug("Adding operations instances {}".format(
operations_instances.keys()))
OperationsService(server, operations_instances)
if reference_storage_instances:
self.logger.debug("Adding reference storages {}".format(
reference_storage_instances.keys()))
ReferenceStorageService(server, reference_storage_instances)
if action_cache_instances:
self.logger.debug("Adding action cache instances {}".format(
action_cache_instances.keys()))
ActionCacheService(server, action_cache_instances)
if cas_instances:
self.logger.debug("Adding cas instances {}".format(
cas_instances.keys()))
ContentAddressableStorageService(server, cas_instances)
if bytestream_instances:
self.logger.debug("Adding bytestream instances {}".format(
bytestream_instances.keys()))
ByteStreamService(server, bytestream_instances)
self._server = server
def start(self):
self._server.start()
def stop(self):
self._server.stop(grace=0)
server:
port: 50051
tls-server-key: null
tls-server-cert: null
tls-client-certs: null
insecure-mode: true
description: |
A single default instance
instances:
- name: main
description: |
The main server
storages:
- !disk-storage &main-storage
path: ~/cas/
services:
- !action-cache &main-action
storage: *main-storage
max_cached_refs: 256
allow_updates: true
- !execution
storage: *main-storage
action_cache: *main-action
- !cas
storage: *main-storage
- !bytestream
storage: *main-storage
# Copyright (C) 2018 Bloomberg LP
#
# 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.
import os
import yaml
from buildgrid.server.controller import ExecutionController
from buildgrid.server.actioncache.storage import ActionCache
from buildgrid.server.cas.instance import ByteStreamInstance, ContentAddressableStorageInstance
from buildgrid.server.cas.storage.disk import DiskStorage
from buildgrid.server.cas.storage.lru_memory_cache import LRUMemoryCache
from buildgrid.server.cas.storage.s3 import S3Storage
from buildgrid.server.cas.storage.with_cache import WithCacheStorage
class YamlFactory(yaml.YAMLObject):
@classmethod
def from_yaml(cls, loader, node):
values = loader.construct_mapping(node, deep=True)
return cls(**values)
class Disk(YamlFactory):
yaml_tag = u'!disk-storage'
def __new__(cls, path):
path = os.path.expanduser(path)
return DiskStorage(path)
class LRU(YamlFactory):
yaml_tag = u'!lru-storage'
def __new__(cls, size):
return LRUMemoryCache(_parse_size(size))
class S3(YamlFactory):
yaml_tag = u'!s3-storage'
def __new__(cls, bucket, endpoint):
return S3Storage(bucket, endpoint_url=endpoint)
class WithCache(YamlFactory):
yaml_tag = u'!with-cache-storage'
def __new__(cls, cache, fallback):
return WithCacheStorage(cache, fallback)
class Execution(YamlFactory):
yaml_tag = u'!execution'
def __new__(cls, storage, action_cache=None):
return ExecutionController(action_cache, storage)
class Action(YamlFactory):
yaml_tag = u'!action-cache'
def __new__(cls, storage, max_cached_refs=0, allow_updates=True):
return ActionCache(storage, max_cached_refs, allow_updates)
class CAS(YamlFactory):
yaml_tag = u'!cas'
def __new__(cls, storage):
return ContentAddressableStorageInstance(storage)
class ByteStream(YamlFactory):
yaml_tag = u'!bytestream'
def __new__(cls, storage):
return ByteStreamInstance(storage)
def _parse_size(size):
"""Convert a string containing a size in bytes (e.g. '2GB') to a number."""
_size_prefixes = {'k': 2 ** 10, 'm': 2 ** 20, 'g': 2 ** 30, 't': 2 ** 40}
size = size.lower()
if size[-1] == 'b':
size = size[:-1]
if size[-1] in _size_prefixes:
return int(size[:-1]) * _size_prefixes[size[-1]]
return int(size)
def get_parser():
yaml.SafeLoader.add_constructor(Execution.yaml_tag, Execution.from_yaml)
yaml.SafeLoader.add_constructor(Execution.yaml_tag, Execution.from_yaml)
yaml.SafeLoader.add_constructor(Action.yaml_tag, Action.from_yaml)
yaml.SafeLoader.add_constructor(Disk.yaml_tag, Disk.from_yaml)
yaml.SafeLoader.add_constructor(LRU.yaml_tag, LRU.from_yaml)
yaml.SafeLoader.add_constructor(S3.yaml_tag, S3.from_yaml)
yaml.SafeLoader.add_constructor(WithCache.yaml_tag, WithCache.from_yaml)
yaml.SafeLoader.add_constructor(CAS.yaml_tag, CAS.from_yaml)
yaml.SafeLoader.add_constructor(ByteStream.yaml_tag, ByteStream.from_yaml)
return yaml
......@@ -27,18 +27,27 @@ import grpc
from buildgrid._protos.build.bazel.remote.execution.v2 import remote_execution_pb2
from buildgrid._protos.build.bazel.remote.execution.v2 import remote_execution_pb2_grpc
from .._exceptions import NotFoundError
from .._exceptions import InvalidArgumentError, NotFoundError
class ActionCacheService(remote_execution_pb2_grpc.ActionCacheServicer):
def __init__(self, action_cache):
self._action_cache = action_cache
def __init__(self, server, instances):
self._instances = instances
self.logger = logging.getLogger(__name__)
remote_execution_pb2_grpc.add_ActionCacheServicer_to_server(self, server)
def GetActionResult(self, request, context):
try:
return self._action_cache.get_action_result(request.action_digest)
instance = self._get_instance(request.instance_name)
return instance.get_action_result(request.action_digest)
except InvalidArgumentError as e:
self.logger.error(e)
context.set_details(str(e))
context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
except NotFoundError as e:
self.logger.error(e)
......@@ -48,11 +57,24 @@ class ActionCacheService(remote_execution_pb2_grpc.ActionCacheServicer):
def UpdateActionResult(self, request, context):
try:
self._action_cache.update_action_result(request.action_digest, request.action_result)
instance = self._get_instance(request.instance_name)
instance.update_action_result(request.action_digest, request.action_result)
return request.action_result
except InvalidArgumentError as e:
self.logger.error(e)
context.set_details(str(e))
context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
except NotImplementedError as e:
self.logger.error(e)
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
return remote_execution_pb2.ActionResult()
def _get_instance(self, instance_name):
try:
return self._instances[instance_name]
except KeyError:
raise InvalidArgumentError("Invalid instance name: {}".format(instance_name))
......@@ -33,10 +33,12 @@ from .._exceptions import InvalidArgumentError, OutofSyncError
class BotsService(bots_pb2_grpc.BotsServicer):
def __init__(self, instances):
def __init__(self, server, instances):
self._instances = instances
self.logger = logging.getLogger(__name__)
bots_pb2_grpc.add_BotsServicer_to_server(self, server)
def CreateBotSession(self, request, context):
try:
parent = request.parent
......
# Copyright (C) 2018 Bloomberg LP
#
# 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.
"""
BuildGridServer
==============
Creates the user a local server BuildGrid server.
"""
from concurrent import futures
import grpc
from buildgrid._protos.google.bytestream import bytestream_pb2_grpc
from buildgrid._protos.build.bazel.remote.execution.v2 import remote_execution_pb2_grpc
from buildgrid._protos.google.devtools.remoteworkers.v1test2 import bots_pb2_grpc
from buildgrid._protos.google.longrunning import operations_pb2_grpc
from .instance import BuildGridInstance
from .cas.service import ByteStreamService, ContentAddressableStorageService
from .actioncache.service import ActionCacheService
from .execution.service import ExecutionService
from .operations.service import OperationsService
from .bots.service import BotsService
class BuildGridServer:
def __init__(self, port=50051, credentials=None, instances=None,
max_workers=10, action_cache=None, cas_storage=None):
address = '[::]:{0}'.format(port)
self._server = grpc.server(futures.ThreadPoolExecutor(max_workers))
if credentials is not None:
self._server.add_secure_port(address, credentials)
else:
self._server.add_insecure_port(address)
if cas_storage is not None:
cas_service = ContentAddressableStorageService(cas_storage)
remote_execution_pb2_grpc.add_ContentAddressableStorageServicer_to_server(cas_service,
self._server)
bytestream_pb2_grpc.add_ByteStreamServicer_to_server(ByteStreamService(cas_storage),
self._server)
if action_cache is not None:
action_cache_service = ActionCacheService(action_cache)
remote_execution_pb2_grpc.add_ActionCacheServicer_to_server(action_cache_service,
self._server)
buildgrid_instances = {}
if not instances:
buildgrid_instances["main"] = BuildGridInstance(action_cache, cas_storage)
else:
for name in instances:
buildgrid_instances[name] = BuildGridInstance(action_cache, cas_storage)
bots_pb2_grpc.add_BotsServicer_to_server(BotsService(buildgrid_instances),
self._server)
remote_execution_pb2_grpc.add_ExecutionServicer_to_server(ExecutionService(buildgrid_instances),
self._server)
operations_pb2_grpc.add_OperationsServicer_to_server(OperationsService(buildgrid_instances),
self._server)
def start(self):
self._server.start()
def stop(self):
self._server.stop(0)
......@@ -27,18 +27,20 @@ import logging
import grpc
from buildgrid._protos.google.bytestream import bytestream_pb2, bytestream_pb2_grpc
from buildgrid._protos.build.bazel.remote.execution.v2 import remote_execution_pb2 as re_pb2
from buildgrid._protos.build.bazel.remote.execution.v2 import remote_execution_pb2_grpc as re_pb2_grpc
from buildgrid._protos.build.bazel.remote.execution.v2 import remote_execution_pb2
from buildgrid._protos.build.bazel.remote.execution.v2 import remote_execution_pb2_grpc
from .._exceptions import InvalidArgumentError, NotFoundError, OutOfRangeError
class ContentAddressableStorageService(re_pb2_grpc.ContentAddressableStorageServicer):
class ContentAddressableStorageService(remote_execution_pb2_grpc.ContentAddressableStorageServicer):
def __init__(self, instances):
def __init__(self, server, instances):
self.logger = logging.getLogger(__name__)
self._instances = instances
remote_execution_pb2_grpc.add_ContentAddressableStorageServicer_to_server(self, server)
def FindMissingBlobs(self, request, context):
try:
instance = self._get_instance(request.instance_name)
......@@ -49,7 +51,7 @@ class ContentAddressableStorageService(re_pb2_grpc.ContentAddressableStorageServ
context.set_details(str(e))
context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
return re_pb2.FindMissingBlobsResponse()
return remote_execution_pb2.FindMissingBlobsResponse()
def BatchUpdateBlobs(self, request, context):
try:
......@@ -61,7 +63,7 @@ class ContentAddressableStorageService(re_pb2_grpc.ContentAddressableStorageServ
context.set_details(str(e))
context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
return re_pb2.BatchReadBlobsResponse()
return remote_execution_pb2.BatchReadBlobsResponse()
def _get_instance(self, instance_name):
try:
......@@ -73,10 +75,12 @@ class ContentAddressableStorageService(re_pb2_grpc.ContentAddressableStorageServ
class ByteStreamService(bytestream_pb2_grpc.ByteStreamServicer):
def __init__(self, instances):
def __init__(self, server, instances):
self.logger = logging.getLogger(__name__)
self._instances = instances
bytestream_pb2_grpc.add_ByteStreamServicer_to_server(self, server)
def Read(self, request, context):
try:
path = request.resource_name.split("/")
......
......@@ -14,31 +14,36 @@
"""
BuildGrid Instance
Execution Controller
==================
An instance of the BuildGrid server.
An instance of the Execution controller.
Contains scheduler, execution instance and an interface to the bots.
All this stuff you need to make the execution service work.
Contains scheduler, execution instance, an interface to the bots
and an operations instance.
"""
import logging
from .execution.instance import ExecutionInstance
from .scheduler import Scheduler
from .bots.instance import BotsInterface
from .execution.instance import ExecutionInstance
from .operations.instance import OperationsInstance
class BuildGridInstance(ExecutionInstance, BotsInterface):
class ExecutionController:
def __init__(self, action_cache=None, cas_storage=None):
def __init__(self, action_cache=None, storage=None):
scheduler = Scheduler(action_cache)
self.logger = logging.getLogger(__name__)
ExecutionInstance.__init__(self, scheduler, cas_storage)
BotsInterface.__init__(self, scheduler)
self._execution_instance = ExecutionInstance(scheduler, storage)
self._bots_interface = BotsInterface(scheduler)
self._operations_instance = OperationsInstance(scheduler)
def stream_operation_updates(self, message_queue, operation_name):
operation = message_queue.get()
......@@ -50,3 +55,15 @@ class BuildGridInstance(ExecutionInstance, BotsInterface):
def cancel_operation(self, name):
# TODO: Cancel leases
raise NotImplementedError("Cancelled operations not supported")
@property
def execution_instance(self):
return self._execution_instance
@property
def bots_interface(self):
return self._bots_interface
@property
def operations_instance(self):
return self._operations_instance
......@@ -24,6 +24,7 @@ import logging
from buildgrid._protos.build.bazel.remote.execution.v2.remote_execution_pb2 import Action
from ..job import Job
from .._exceptions import InvalidArgumentError
class ExecutionInstance:
......@@ -51,3 +52,24 @@ class ExecutionInstance:
self._scheduler.append_job(job, skip_cache_lookup)
return job.get_operation()
def register_message_client(self, name, queue):
try:
self._scheduler.register_client(name, queue)
except KeyError:
raise InvalidArgumentError("Operation name does not exist: {}".format(name))
def unregister_message_client(self, name, queue):
try:
self._scheduler.unregister_client(name, queue)
except KeyError:
raise InvalidArgumentError("Operation name does not exist: {}".format(name))
def stream_operation_updates(self, message_queue, operation_name):
operation = message_queue.get()
while not operation.done:
yield operation
operation = message_queue.get()
yield operation
......@@ -35,10 +35,12 @@ from .._exceptions import InvalidArgumentError
class ExecutionService(remote_execution_pb2_grpc.ExecutionServicer):
def __init__(self, instances):
def __init__(self, server, instances):
self.logger = logging.getLogger(__name__)
self._instances = instances
remote_execution_pb2_grpc.add_ExecutionServicer_to_server(self, server)
def Execute(self, request, context):
try:
message_queue = queue.Queue()
......
......@@ -64,3 +64,14 @@ class OperationsInstance:
except KeyError:
raise InvalidArgumentError("Operation name does not exist: {}".format(name))
def stream_operation_updates(self, message_queue, operation_name):
operation = message_queue.get()
while not operation.done:
yield operation
operation = message_queue.get()
yield operation
def cancel_operation(self, name):
# TODO: Cancel leases
raise NotImplementedError("Cancelled operations not supported")
......@@ -32,10 +32,12 @@ from .._exceptions import InvalidArgumentError
class OperationsService(operations_pb2_grpc.OperationsServicer):
def __init__(self, instances):
def __init__(self, server, instances):
self._instances = instances
self.logger = logging.getLogger(__name__)
operations_pb2_grpc.add_OperationsServicer_to_server(self, server)
def GetOperation(self, request, context):
try:
name = request.name
......
......@@ -20,34 +20,70 @@ import grpc
from buildgrid._protos.buildstream.v2 import buildstream_pb2
from buildgrid._protos.buildstream.v2 import buildstream_pb2_grpc
from .._exceptions import NotFoundError
from .._exceptions import InvalidArgumentError, NotFoundError
class ReferenceStorageService(buildstream_pb2_grpc.ReferenceStorageServicer):
def __init__(self, reference_cache):
self._reference_cache = reference_cache
def __init__(self, server, instances):
self.logger = logging.getLogger(__name__)
self._instances = instances
buildstream_pb2_grpc.add_ReferenceStorageServicer_to_server(self, server)
def GetReference(self, request, context):
try:
instance = self._get_instance(request.instance_name)
digest = instance.get_digest_reference(request.key)
response = buildstream_pb2.GetReferenceResponse()
response.digest.CopyFrom(self._reference_cache.get_digest_reference(request.key))
response.digest.CopyFrom(digest)
return response
except InvalidArgumentError as e:
self.logger.error(e)
context.set_details(str(e))
context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
except NotFoundError:
context.set_code(grpc.StatusCode.NOT_FOUND)
return buildstream_pb2.GetReferenceResponse()
def UpdateReference(self, request, context):
try:
instance = self._get_instance(request.instance_name)
digest = request.digest
for key in request.keys:
self._reference_cache.update_reference(key, request.digest)
instance.update_reference(key, digest)
return buildstream_pb2.UpdateReferenceResponse()
except InvalidArgumentError as e:
self.logger.error(e)
context.set_details(str(e))
context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
except NotImplementedError:
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
return buildstream_pb2.UpdateReferenceResponse()
def Status(self, request, context):
allow_updates = self._reference_cache.allow_updates
return buildstream_pb2.StatusResponse(allow_updates=allow_updates)
try:
instance = self._get_instance(request.instance_name)
allow_updates = instance.allow_updates
return buildstream_pb2.StatusResponse(allow_updates=allow_updates)
except InvalidArgumentError as e:
self.logger.error(e)
context.set_details(str(e))
context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
return buildstream_pb2.StatusResponse()
def _get_instance(self, instance_name):
try:
return self._instances[instance_name]
except KeyError:
raise InvalidArgumentError("Invalid instance name: {}".format(instance_name))
.. _dummy-build:
Dummy build
......@@ -8,7 +7,7 @@ In one terminal, start a server:
.. code-block:: sh
bgd server start --allow-insecure
bgd server start buildgrid/_app/settings/default.yml
In another terminal, send a request for work:
......
.. _simple-build:
Simple build
......@@ -27,7 +26,7 @@ Now start a BuildGrid server, passing it a directory it can write a CAS to:
.. code-block:: sh
bgd server start --allow-insecure --cas disk --cas-cache disk --cas-disk-directory /path/to/empty/directory
bgd server start buildgrid/_app/settings/default.yml
Start the following bot session:
......