Пример #1
0
def load_tls_channel_credentials(client_key=None,
                                 client_cert=None,
                                 server_cert=None):
    """Looks-up and loads TLS gRPC client channel credentials.

    Args:
        client_key(str, optional): Client certificate chain file path.
        client_cert(str, optional): Client private key file path.
        server_cert(str, optional): Serve root certificate file path.

    Returns:
        ChannelCredentials: Credentials to be used for a TLS-encrypted gRPC
            client channel.
    """
    if server_cert and os.path.exists(server_cert):
        server_cert_pem = read_file(server_cert)
    else:
        server_cert_pem = None
        server_cert = None

    if client_key and os.path.exists(client_key):
        client_key_pem = read_file(client_key)
    else:
        client_key_pem = None
        client_key = None

    if client_key_pem and client_cert and os.path.exists(client_cert):
        client_cert_pem = read_file(client_cert)
    else:
        client_cert_pem = None
        client_cert = None

    credentials = grpc.ssl_channel_credentials(
        root_certificates=server_cert_pem,
        private_key=client_key_pem,
        certificate_chain=client_cert_pem)

    return credentials, (
        client_key,
        client_cert,
        server_cert,
    )
Пример #2
0
    def load_server_credentials(self, server_key=None, server_cert=None, client_certs=None):
        """Looks-up and loads TLS server gRPC credentials.

        Every private and public keys are expected to be PEM-encoded.

        Args:
            server_key(str): private server key file path.
            server_cert(str): public server certificate file path.
            client_certs(str): public client certificates file path.

        Returns:
            :obj:`ServerCredentials`: The credentials for use for a
            TLS-encrypted gRPC server channel.
        """
        if not server_key or not os.path.exists(server_key):
            return None

        if not server_cert or not os.path.exists(server_cert):
            return None

        server_key_pem = read_file(server_key)
        server_cert_pem = read_file(server_cert)
        if client_certs and os.path.exists(client_certs):
            client_certs_pem = read_file(client_certs)
        else:
            client_certs_pem = None
            client_certs = None

        credentials = grpc.ssl_server_credentials([(server_key_pem, server_cert_pem)],
                                                  root_certificates=client_certs_pem,
                                                  require_client_auth=bool(client_certs))

        credentials.server_key = server_key
        credentials.server_cert = server_cert
        credentials.client_certs = client_certs

        return credentials
Пример #3
0
    def load_client_credentials(self, client_key=None, client_cert=None, server_cert=None):
        """Looks-up and loads TLS client gRPC credentials.

        Args:
            client_key(str): root certificate file path.
            client_cert(str): private key file path.
            server_cert(str): certificate chain file path.

        Returns:
            :obj:`ChannelCredentials`: The credentials for use for a
            TLS-encrypted gRPC client channel.
        """

        if not server_cert or not os.path.exists(server_cert):
            return None

        server_cert_pem = read_file(server_cert)
        if client_key and os.path.exists(client_key):
            client_key_pem = read_file(client_key)
        else:
            client_key_pem = None
            client_key = None
        if client_key_pem and client_cert and os.path.exists(client_cert):
            client_cert_pem = read_file(client_cert)
        else:
            client_cert_pem = None
            client_cert = None

        credentials = grpc.ssl_channel_credentials(root_certificates=server_cert_pem,
                                                   private_key=client_key_pem,
                                                   certificate_chain=client_cert_pem)

        credentials.client_key = client_key
        credentials.client_cert = client_cert
        credentials.server_cert = server_cert

        return credentials
Пример #4
0
def load_channel_authorization_token(auth_token=None):
    """Looks-up and loads client authorization token.

    Args:
        auth_token (str, optional): Token file path.

    Returns:
        str: Encoded token string.
    """
    if auth_token and os.path.exists(auth_token):
        return read_file(auth_token).decode()

    # TODO: Try loading the token from a default location?

    return None
Пример #5
0
def download_file(context, digest_path_list, verify):
    # Downloading files:
    downloaded_files = {}
    try:
        with download(context.channel,
                      instance=context.instance_name) as downloader:
            for (digest_string, file_path) in zip(digest_path_list[0::2],
                                                  digest_path_list[1::2]):
                if os.path.exists(file_path):
                    click.echo("Error: Invalid value for " +
                               "path=[{}] already exists.".format(file_path),
                               err=True)
                    continue

                digest = parse_digest(digest_string)

                downloader.download_file(digest, file_path)
                downloaded_files[file_path] = digest
    except Exception as e:
        click.echo('Error: Downloading file: {}'.format(e), err=True)
        sys.exit(-1)
    except FileNotFoundError:
        click.echo('Error: Blob not found in CAS', err=True)
        sys.exit(-1)

    # Verifying:
    for (file_path, digest) in downloaded_files.items():
        if verify:
            file_digest = create_digest(read_file(file_path))
            if file_digest != digest:
                click.echo(
                    "Error: Failed to verify path=[{}]".format(file_path),
                    err=True)
                continue

        if os.path.isfile(file_path):
            click.echo("Success: Pulled path=[{}] from digest=[{}/{}]".format(
                file_path, digest.hash, digest.size_bytes))
        else:
            click.echo('Error: Failed pulling "{}"'.format(file_path),
                       err=True)
Пример #6
0
def work_buildbox(lease, context, event):
    """Executes a lease for a build action, using buildbox.
    """
    local_cas_directory = context.local_cas
    # instance_name = context.parent

    logger = logging.getLogger(__name__)

    action_digest = remote_execution_pb2.Digest()

    lease.payload.Unpack(action_digest)
    lease.result.Clear()

    with download(context.cas_channel) as downloader:
        action = downloader.get_message(action_digest,
                                        remote_execution_pb2.Action())

        assert action.command_digest.hash

        command = downloader.get_message(action.command_digest,
                                         remote_execution_pb2.Command())

    if command.working_directory:
        working_directory = command.working_directory
    else:
        working_directory = '/'

    logger.debug("Command digest: [{}/{}]"
                 .format(action.command_digest.hash, action.command_digest.size_bytes))
    logger.debug("Input root digest: [{}/{}]"
                 .format(action.input_root_digest.hash, action.input_root_digest.size_bytes))

    os.makedirs(os.path.join(local_cas_directory, 'tmp'), exist_ok=True)
    os.makedirs(context.fuse_dir, exist_ok=True)
    tempdir = os.path.join(local_cas_directory, 'tmp')

    with tempfile.NamedTemporaryFile(dir=tempdir) as input_digest_file:
        # Input hash must be written to disk for BuildBox
        write_file(input_digest_file.name, action.input_root_digest.SerializeToString())

        with tempfile.NamedTemporaryFile(dir=tempdir) as output_digest_file:
            with tempfile.NamedTemporaryFile(dir=tempdir) as timestamps_file:
                command_line = ['buildbox',
                                '--remote={}'.format(context.remote_cas_url),
                                '--input-digest={}'.format(input_digest_file.name),
                                '--output-digest={}'.format(output_digest_file.name),
                                '--chdir={}'.format(working_directory),
                                '--local={}'.format(local_cas_directory),
                                '--output-times={}'.format(timestamps_file.name)]

                if context.cas_client_key:
                    command_line.append('--client-key={}'.format(context.cas_client_key))
                if context.cas_client_cert:
                    command_line.append('--client-cert={}'.format(context.cas_client_cert))
                if context.cas_server_cert:
                    command_line.append('--server-cert={}'.format(context.cas_server_cert))

                command_line.append('--clearenv')
                for variable in command.environment_variables:
                    command_line.append('--setenv')
                    command_line.append(variable.name)
                    command_line.append(variable.value)

                command_line.append(context.fuse_dir)
                command_line.extend(command.arguments)

                logger.info("Starting execution: [{}...]".format(command.arguments[0]))

                command_line = subprocess.Popen(command_line,
                                                stdin=subprocess.PIPE,
                                                stdout=subprocess.PIPE,
                                                stderr=subprocess.PIPE)
                stdout, stderr = command_line.communicate()
                returncode = command_line.returncode

                action_result = remote_execution_pb2.ActionResult()
                action_result.exit_code = returncode

                logger.info("Execution finished with code: [{}]".format(returncode))

                output_digest = remote_execution_pb2.Digest()
                output_digest.ParseFromString(read_file(output_digest_file.name))

                logger.debug("Output root digest: [{}/{}]"
                             .format(output_digest.hash, output_digest.size_bytes))

                metadata = read_file(timestamps_file.name)
                logger.debug("metadata: {}".format(metadata))
                action_result.execution_metadata.ParseFromString(metadata)

                if len(output_digest.hash) != HASH_LENGTH:
                    raise BotError(
                        stdout, detail=stderr, reason="Output root digest too small.")

                # TODO: Have BuildBox helping us creating the Tree instance here
                # See https://gitlab.com/BuildStream/buildbox/issues/7 for details
                with download(context.cas_channel) as downloader:
                    output_tree = _cas_tree_maker(downloader, output_digest)

                with upload(context.cas_channel) as uploader:
                    output_tree_digest = uploader.put_message(output_tree)

                    output_directory = remote_execution_pb2.OutputDirectory()
                    output_directory.tree_digest.CopyFrom(output_tree_digest)
                    output_directory.path = os.path.relpath(working_directory, start='/')

                    action_result.output_directories.extend([output_directory])

                    if action_result.ByteSize() + len(stdout) > MAX_REQUEST_SIZE:
                        stdout_digest = uploader.put_blob(stdout)
                        action_result.stdout_digest.CopyFrom(stdout_digest)

                    else:
                        action_result.stdout_raw = stdout

                    if action_result.ByteSize() + len(stderr) > MAX_REQUEST_SIZE:
                        stderr_digest = uploader.put_blob(stderr)
                        action_result.stderr_digest.CopyFrom(stderr_digest)

                    else:
                        action_result.stderr_raw = stderr

                lease.result.Pack(action_result)

    return lease
Пример #7
0
def _create_server_from_config(configuration):
    """Parses configuration and setup a fresh server instance."""
    kargs = {}

    try:
        network = configuration['server']
        instances = configuration['instances']

    except KeyError as e:
        click.echo("Error: Section missing from configuration: {}.".format(e),
                   err=True)
        sys.exit(-1)

    if 'authorization' in configuration:
        authorization = configuration['authorization']

        try:
            if 'method' in authorization:
                kargs['auth_method'] = AuthMetadataMethod(
                    authorization['method'])

            if 'secret' in authorization:
                kargs['auth_secret'] = read_file(
                    authorization['secret']).decode().strip()

            if 'algorithm' in authorization:
                kargs['auth_algorithm'] = AuthMetadataAlgorithm(
                    authorization['algorithm'])

        except (ValueError, OSError) as e:
            click.echo("Error: Configuration, {}.".format(e), err=True)
            sys.exit(-1)

    if 'monitoring' in configuration:
        monitoring = configuration['monitoring']

        try:
            if 'enabled' in monitoring:
                kargs['monitor'] = monitoring['enabled']

            if 'endpoint-type' in monitoring:
                kargs['mon_endpoint_type'] = MonitoringOutputType(
                    monitoring['endpoint-type'])

            if 'endpoint-location' in monitoring:
                kargs['mon_endpoint_location'] = monitoring[
                    'endpoint-location']

            if 'serialization-format' in monitoring:
                kargs['mon_serialisation_format'] = MonitoringOutputFormat(
                    monitoring['serialization-format'])

            if 'metric-prefix' in monitoring:
                # Ensure there's only one period at the end of the prefix
                kargs['mon_metric_prefix'] = monitoring['metric-prefix'].strip(
                ).rstrip('.') + "."

        except (ValueError, OSError) as e:
            click.echo("Error: Configuration, {}.".format(e), err=True)
            sys.exit(-1)

    if 'thread-pool-size' in configuration:
        try:
            kargs['max_workers'] = int(configuration['thread-pool-size'])

        except ValueError as e:
            click.echo("Error: Configuration, {}.".format(e), err=True)
            sys.exit(-1)

    server = Server(**kargs)

    try:
        for channel in network:
            server.add_port(channel.address, channel.credentials)

    except PermissionDeniedError as e:
        click.echo("Error: {}.".format(e), err=True)
        sys.exit(-1)

    for instance in instances:
        instance_name = instance['name']
        services = instance['services']

        for service in services:
            service.register_instance_with_server(instance_name, server)

    return server