Martin Blanchard pushed to branch mablanch/68-bots-multi-endpoints at BuildGrid / buildgrid
Commits:
-
efe77c95
by Martin Blanchard at 2018-08-29T13:33:08Z
4 changed files:
- buildgrid/_app/bots/buildbox.py
- buildgrid/_app/bots/temp_directory.py
- buildgrid/_app/cli.py
- buildgrid/_app/commands/cmd_bot.py
Changes:
| ... | ... | @@ -16,13 +16,12 @@ |
| 16 | 16 |
import os
|
| 17 | 17 |
import subprocess
|
| 18 | 18 |
import tempfile
|
| 19 |
-import grpc
|
|
| 20 | 19 |
|
| 21 | 20 |
from google.protobuf import any_pb2
|
| 22 | 21 |
|
| 23 | 22 |
from buildgrid._protos.build.bazel.remote.execution.v2 import remote_execution_pb2
|
| 24 | 23 |
from buildgrid._protos.google.bytestream import bytestream_pb2_grpc
|
| 25 |
-from buildgrid.utils import read_file, parse_to_pb2_from_fetch
|
|
| 24 |
+from buildgrid.utils import parse_to_pb2_from_fetch
|
|
| 26 | 25 |
|
| 27 | 26 |
|
| 28 | 27 |
def work_buildbox(context, lease):
|
| ... | ... | @@ -32,18 +31,7 @@ def work_buildbox(context, lease): |
| 32 | 31 |
action_digest = remote_execution_pb2.Digest()
|
| 33 | 32 |
action_digest_any.Unpack(action_digest)
|
| 34 | 33 |
|
| 35 |
- cert_server = read_file(context.server_cert)
|
|
| 36 |
- cert_client = read_file(context.client_cert)
|
|
| 37 |
- key_client = read_file(context.client_key)
|
|
| 38 |
- |
|
| 39 |
- # create server credentials
|
|
| 40 |
- credentials = grpc.ssl_channel_credentials(root_certificates=cert_server,
|
|
| 41 |
- private_key=key_client,
|
|
| 42 |
- certificate_chain=cert_client)
|
|
| 43 |
- |
|
| 44 |
- channel = grpc.secure_channel('{}:{}'.format(context.remote, context.port), credentials)
|
|
| 45 |
- |
|
| 46 |
- stub = bytestream_pb2_grpc.ByteStreamStub(channel)
|
|
| 34 |
+ stub = bytestream_pb2_grpc.ByteStreamStub(context.cas_channel)
|
|
| 47 | 35 |
|
| 48 | 36 |
action = remote_execution_pb2.Action()
|
| 49 | 37 |
parse_to_pb2_from_fetch(action, stub, action_digest)
|
| ... | ... | @@ -66,13 +54,18 @@ def work_buildbox(context, lease): |
| 66 | 54 |
|
| 67 | 55 |
with tempfile.NamedTemporaryFile(dir=os.path.join(casdir, 'tmp')) as output_digest_file:
|
| 68 | 56 |
command = ['buildbox',
|
| 69 |
- '--remote={}'.format('https://{}:{}'.format(context.remote, context.port)),
|
|
| 70 |
- '--server-cert={}'.format(context.server_cert),
|
|
| 71 |
- '--client-key={}'.format(context.client_key),
|
|
| 72 |
- '--client-cert={}'.format(context.client_cert),
|
|
| 57 |
+ '--remote={}'.format(context.cas_remote_url),
|
|
| 73 | 58 |
'--input-digest={}'.format(input_digest_file.name),
|
| 74 | 59 |
'--output-digest={}'.format(output_digest_file.name),
|
| 75 | 60 |
'--local={}'.format(casdir)]
|
| 61 |
+ |
|
| 62 |
+ if context.cas_client_key:
|
|
| 63 |
+ command.append('--client-key={}'.format(context.cas_client_key))
|
|
| 64 |
+ if context.cas_client_cert:
|
|
| 65 |
+ command.append('--client-cert={}'.format(context.cas_client_cert))
|
|
| 66 |
+ if context.cas_server_cert:
|
|
| 67 |
+ command.append('--server-cert={}'.format(context.cas_server_cert))
|
|
| 68 |
+ |
|
| 76 | 69 |
if 'PWD' in environment and environment['PWD']:
|
| 77 | 70 |
command.append('--chdir={}'.format(environment['PWD']))
|
| 78 | 71 |
|
| ... | ... | @@ -30,7 +30,7 @@ def work_temp_directory(context, lease): |
| 30 | 30 |
"""
|
| 31 | 31 |
|
| 32 | 32 |
parent = context.parent
|
| 33 |
- stub_bytestream = bytestream_pb2_grpc.ByteStreamStub(context.channel)
|
|
| 33 |
+ stub_bytestream = bytestream_pb2_grpc.ByteStreamStub(context.cas_channel)
|
|
| 34 | 34 |
|
| 35 | 35 |
action_digest = remote_execution_pb2.Digest()
|
| 36 | 36 |
lease.payload.Unpack(action_digest)
|
| ... | ... | @@ -78,7 +78,7 @@ def work_temp_directory(context, lease): |
| 78 | 78 |
request = remote_execution_pb2.BatchUpdateBlobsRequest(instance_name=parent,
|
| 79 | 79 |
requests=requests)
|
| 80 | 80 |
|
| 81 |
- stub_cas = remote_execution_pb2_grpc.ContentAddressableStorageStub(context.channel)
|
|
| 81 |
+ stub_cas = remote_execution_pb2_grpc.ContentAddressableStorageStub(context.cas_channel)
|
|
| 82 | 82 |
stub_cas.BatchUpdateBlobs(request)
|
| 83 | 83 |
|
| 84 | 84 |
result_any = any_pb2.Any()
|
| ... | ... | @@ -83,14 +83,22 @@ class Context: |
| 83 | 83 |
client_key_pem = read_file(client_key)
|
| 84 | 84 |
else:
|
| 85 | 85 |
client_key_pem = None
|
| 86 |
+ client_key = None
|
|
| 86 | 87 |
if client_key_pem and client_cert and os.path.exists(client_cert):
|
| 87 | 88 |
client_cert_pem = read_file(client_cert)
|
| 88 | 89 |
else:
|
| 89 | 90 |
client_cert_pem = None
|
| 91 |
+ client_cert = None
|
|
| 90 | 92 |
|
| 91 |
- return grpc.ssl_channel_credentials(root_certificates=server_cert_pem,
|
|
| 92 |
- private_key=client_key_pem,
|
|
| 93 |
- certificate_chain=client_cert_pem)
|
|
| 93 |
+ credentials = grpc.ssl_channel_credentials(root_certificates=server_cert_pem,
|
|
| 94 |
+ private_key=client_key_pem,
|
|
| 95 |
+ certificate_chain=client_cert_pem)
|
|
| 96 |
+ |
|
| 97 |
+ credentials.client_key = client_key
|
|
| 98 |
+ credentials.client_cert = client_cert
|
|
| 99 |
+ credentials.server_cert = server_cert
|
|
| 100 |
+ |
|
| 101 |
+ return credentials
|
|
| 94 | 102 |
|
| 95 | 103 |
def load_server_credentials(self, server_key=None, server_cert=None,
|
| 96 | 104 |
client_certs=None, use_default_client_certs=False):
|
| ... | ... | @@ -132,10 +140,17 @@ class Context: |
| 132 | 140 |
client_certs_pem = read_file(client_certs)
|
| 133 | 141 |
else:
|
| 134 | 142 |
client_certs_pem = None
|
| 143 |
+ client_certs = None
|
|
| 144 |
+ |
|
| 145 |
+ credentials = grpc.ssl_server_credentials([(server_key_pem, server_cert_pem)],
|
|
| 146 |
+ root_certificates=client_certs_pem,
|
|
| 147 |
+ require_client_auth=bool(client_certs))
|
|
| 148 |
+ |
|
| 149 |
+ credentials.server_key = server_key
|
|
| 150 |
+ credentials.server_cert = server_cert
|
|
| 151 |
+ credentials.client_certs = client_certs
|
|
| 135 | 152 |
|
| 136 |
- return grpc.ssl_server_credentials([(server_key_pem, server_cert_pem)],
|
|
| 137 |
- root_certificates=client_certs_pem,
|
|
| 138 |
- require_client_auth=bool(client_certs))
|
|
| 153 |
+ return credentials
|
|
| 139 | 154 |
|
| 140 | 155 |
|
| 141 | 156 |
pass_context = click.make_pass_decorator(Context, ensure=True)
|
| ... | ... | @@ -44,26 +44,79 @@ from ..cli import pass_context |
| 44 | 44 |
help="Public client certificate for TLS (PEM-encoded)")
|
| 45 | 45 |
@click.option('--server-cert', type=click.Path(exists=True, dir_okay=False), default=None,
|
| 46 | 46 |
help="Public server certificate for TLS (PEM-encoded)")
|
| 47 |
+@click.option('--remote-cas', type=click.STRING, default=None, show_default=True,
|
|
| 48 |
+ help="Remote CAS server's URL (port defaults to 11001 if not specified).")
|
|
| 49 |
+@click.option('--cas-client-key', type=click.Path(exists=True, dir_okay=False), default=None,
|
|
| 50 |
+ help="Private CAS client key for TLS (PEM-encoded)")
|
|
| 51 |
+@click.option('--cas-client-cert', type=click.Path(exists=True, dir_okay=False), default=None,
|
|
| 52 |
+ help="Public CAS client certificate for TLS (PEM-encoded)")
|
|
| 53 |
+@click.option('--cas-server-cert', type=click.Path(exists=True, dir_okay=False), default=None,
|
|
| 54 |
+ help="Public CAS server certificate for TLS (PEM-encoded)")
|
|
| 47 | 55 |
@click.option('--parent', type=click.STRING, default='main', show_default=True,
|
| 48 | 56 |
help="Targeted farm resource.")
|
| 49 | 57 |
@pass_context
|
| 50 |
-def cli(context, remote, parent, client_key, client_cert, server_cert):
|
|
| 58 |
+def cli(context, parent, remote, client_key, client_cert, server_cert,
|
|
| 59 |
+ cas_remote, cas_client_key, cas_client_cert, cas_server_cert):
|
|
| 60 |
+ # Setup the remote execution server channel:
|
|
| 51 | 61 |
url = urlparse(remote)
|
| 52 | 62 |
|
| 53 | 63 |
context.remote = '{}:{}'.format(url.hostname, url.port or 50051)
|
| 64 |
+ context.remote_url = remote
|
|
| 54 | 65 |
context.parent = parent
|
| 55 | 66 |
|
| 56 | 67 |
if url.scheme == 'http':
|
| 57 | 68 |
context.channel = grpc.insecure_channel(context.remote)
|
| 69 |
+ |
|
| 70 |
+ context.client_key = None
|
|
| 71 |
+ context.client_cert = None
|
|
| 72 |
+ context.server_cert = None
|
|
| 58 | 73 |
else:
|
| 59 | 74 |
credentials = context.load_client_credentials(client_key, client_cert, server_cert)
|
| 60 | 75 |
if not credentials:
|
| 61 |
- click.echo("ERROR: no TLS keys were specified and no defaults could be found.\n" +
|
|
| 62 |
- "Use --allow-insecure in order to deactivate TLS encryption.\n", err=True)
|
|
| 76 |
+ click.echo("ERROR: no TLS keys were specified and no defaults could be found.", err=True)
|
|
| 63 | 77 |
sys.exit(-1)
|
| 64 | 78 |
|
| 65 | 79 |
context.channel = grpc.secure_channel(context.remote, credentials)
|
| 66 | 80 |
|
| 81 |
+ context.client_key = credentials.client_key
|
|
| 82 |
+ context.client_cert = credentials.client_cert
|
|
| 83 |
+ context.server_cert = credentials.server_cert
|
|
| 84 |
+ |
|
| 85 |
+ # Setup the remote CAS server channel, if separated:
|
|
| 86 |
+ if cas_remote is not None and cas_remote != remote:
|
|
| 87 |
+ cas_url = urlparse(cas_remote)
|
|
| 88 |
+ |
|
| 89 |
+ context.cas_remote = '{}:{}'.format(cas_url.hostname, cas_url.port or 11001)
|
|
| 90 |
+ context.cas_remote_url = cas_remote
|
|
| 91 |
+ |
|
| 92 |
+ if cas_url.scheme == 'http':
|
|
| 93 |
+ context.cas_channel = grpc.insecure_channel(context.cas_remote)
|
|
| 94 |
+ |
|
| 95 |
+ context.cas_client_key = None
|
|
| 96 |
+ context.cas_client_cert = None
|
|
| 97 |
+ context.cas_server_cert = None
|
|
| 98 |
+ else:
|
|
| 99 |
+ cas_credentials = context.load_client_credentials(cas_client_key, cas_client_cert, cas_server_cert)
|
|
| 100 |
+ if not cas_credentials:
|
|
| 101 |
+ click.echo("ERROR: no TLS keys were specified and no defaults could be found.", err=True)
|
|
| 102 |
+ sys.exit(-1)
|
|
| 103 |
+ |
|
| 104 |
+ context.cas_channel = grpc.secure_channel(context.cas_remote, cas_credentials)
|
|
| 105 |
+ |
|
| 106 |
+ context.cas_client_key = cas_credentials.client_key
|
|
| 107 |
+ context.cas_client_cert = cas_credentials.client_cert
|
|
| 108 |
+ context.cas_server_cert = cas_credentials.server_cert
|
|
| 109 |
+ |
|
| 110 |
+ else:
|
|
| 111 |
+ context.cas_remote = context.remote
|
|
| 112 |
+ context.cas_remote_url = remote
|
|
| 113 |
+ |
|
| 114 |
+ context.cas_channel = context.channel
|
|
| 115 |
+ |
|
| 116 |
+ context.cas_client_key = context.client_key
|
|
| 117 |
+ context.cas_client_cert = context.client_cert
|
|
| 118 |
+ context.cas_server_cert = context.server_cert
|
|
| 119 |
+ |
|
| 67 | 120 |
context.logger = logging.getLogger(__name__)
|
| 68 | 121 |
context.logger.debug("Starting for remote {}".format(context.remote))
|
| 69 | 122 |
|
| ... | ... | @@ -112,35 +165,17 @@ def run_temp_directory(context): |
| 112 | 165 |
help="Main mount-point location.")
|
| 113 | 166 |
@click.option('--local-cas', type=click.Path(readable=False), default=str(PurePath(Path.home(), 'cas')),
|
| 114 | 167 |
help="Local CAS cache directory.")
|
| 115 |
-@click.option('--client-cert', type=click.Path(readable=False), default=str(PurePath(Path.home(), 'client.crt')),
|
|
| 116 |
- help="Public client certificate for TLS (PEM-encoded).")
|
|
| 117 |
-@click.option('--client-key', type=click.Path(readable=False), default=str(PurePath(Path.home(), 'client.key')),
|
|
| 118 |
- help="Private client key for TLS (PEM-encoded).")
|
|
| 119 |
-@click.option('--server-cert', type=click.Path(readable=False), default=str(PurePath(Path.home(), 'server.crt')),
|
|
| 120 |
- help="Public server certificate for TLS (PEM-encoded).")
|
|
| 121 |
-@click.option('--port', type=click.INT, default=11001, show_default=True,
|
|
| 122 |
- help="Remote CAS server port.")
|
|
| 123 |
-@click.option('--remote', type=click.STRING, default='localhost', show_default=True,
|
|
| 124 |
- help="Remote CAS server hostname.")
|
|
| 125 | 168 |
@pass_context
|
| 126 |
-def run_buildbox(context, remote, port, server_cert, client_key, client_cert, local_cas, fuse_dir):
|
|
| 169 |
+def run_buildbox(context, local_cas, fuse_dir):
|
|
| 127 | 170 |
"""
|
| 128 | 171 |
Uses BuildBox to run commands.
|
| 129 | 172 |
"""
|
| 130 |
- |
|
| 131 |
- context.logger.info("Creating a bot session")
|
|
| 132 |
- |
|
| 133 |
- context.remote = remote
|
|
| 134 |
- context.port = port
|
|
| 135 |
- context.server_cert = server_cert
|
|
| 136 |
- context.client_key = client_key
|
|
| 137 |
- context.client_cert = client_cert
|
|
| 138 | 173 |
context.local_cas = local_cas
|
| 139 | 174 |
context.fuse_dir = fuse_dir
|
| 140 | 175 |
|
| 141 | 176 |
try:
|
| 142 | 177 |
b = bot.Bot(context.bot_session)
|
| 143 |
- b.session(work=buildbox.work_buildbox,
|
|
| 144 |
- context=context)
|
|
| 178 |
+ b.session(buildbox.work_buildbox,
|
|
| 179 |
+ context)
|
|
| 145 | 180 |
except KeyboardInterrupt:
|
| 146 | 181 |
pass
|
