Beispiel #1
0
def verify(config):
    """Verify the configuration contain the necessary details."""
    errors = []

    if 'build' in config:
        for error in verify_build(config['build']):
            errors.append(error)
    else:
        utils.debug('No "build" section found in configuration.')

    for error in verify_package(config):
        errors.append(error)

    for section in {'build', 'package'}:
        for local_path, container_path in config.get(section, dict()).get('volumes', dict()).items():
            if not path.exists(local_path) and not create_local_directory(local_path):
                errors.append('The path "{}" specified as a {} volume does not exist on the local machine.'.format(local_path, section))
            if not path.isabs(container_path):
                errors.append('The path "{}" specified as a {} volume has to be absolute.'.format(container_path, section))

    if 'environment' in config and not isinstance(config['environment'], dict):
        errors.append('The environment variables should be described as a dictionary.')

    if errors:
        utils.log('The following errors were encountered while verifying the configuration:', err=True, fg='red', bold=True)
        for error in errors:
            utils.log('  - {}'.format(error), err=True, prefix=False)
        return False
    else:
        utils.info('The configuration has been verified without errors.')
        return True
Beispiel #2
0
def create_build_script(script):
    """Write the build script to a temporary file."""
    utils.debug('Writing build script to temporary file.')
    with NamedTemporaryFile(prefix='berth-build-', suffix='.sh', delete=False, dir='.') as script_file:
        script_file.write(script.encode('utf-8'))
        script_path = script_file.name
    chmod(script_path, 493)  # Add execute permissions: 493 = rwxr-xr-x
    utils.info('Wrote temporary build script to "{}".'.format(script_path))
    return script_path
Beispiel #3
0
def create_local_directory(local_path):
    """Create a local directory for a specified path that doesn't exist."""
    try:
        utils.debug('Creating a directory at the local path "{}".'.format(local_path))
        mkdir(local_path)
        utils.info('Created directory at "{}" as the path did not exist.'.format(local_path))
        return True
    except PermissionError:
        return False
Beispiel #4
0
def create_build_script(script):
    """Write the build script to a temporary file."""
    utils.debug('Writing build script to temporary file.')
    with NamedTemporaryFile(prefix='berth-build-', suffix='.sh', delete=False, dir='.') as script_file:
        script_file.write(script.encode('utf-8'))
        script_path = script_file.name
    chmod(script_path, 493)  # Add execute permissions: 493 = rwxr-xr-x
    utils.info('Wrote temporary build script to "{}".'.format(script_path))
    return script_path
Beispiel #5
0
def create_local_directory(local_path):
    """Create a local directory for a specified path that doesn't exist."""
    try:
        utils.debug(
            'Creating a directory at the local path "{}".'.format(local_path))
        mkdir(local_path)
        utils.info(
            'Created directory at "{}" as the path did not exist.'.format(
                local_path))
        return True
    except PermissionError:
        return False
Beispiel #6
0
def build(config):
    """Run the build part of the configuration."""
    utils.log('Building project.')

    script_path = create_build_script(config['build']['script'])
    script_container_path = '/{}'.format(path.basename(script_path))

    volumes = config['build'].get('volumes', dict())
    volumes[script_path] = script_container_path
    binds = utils.convert_volumes_list(volumes)

    docker = utils.docker_client()
    container = docker.create_container(
        image=config['build']['image'],
        command=[script_container_path],
        environment={str(k): str(v) for k, v in config.get('environment', dict()).items()},
        host_config=docker.create_host_config(binds=binds)
    )

    if container['Warnings']:
        utils.log('We got a warning when creating the build container:', err=True, fg='red', bold=True)
        utils.log(str(container['Warnings']), err=True, prefix=False)
        remove_build_script(script_path)
        return False

    utils.debug('Starting build container.')
    start_time = time.time()
    docker.start(container['Id'])
    utils.info('Build container started.')

    if utils.get_log_level() > 0:
        for line in docker.logs(container['Id'], stream=True):
            utils.log(line.decode('utf-8').rstrip(), prefix=False)

    exit_code = docker.wait(container['Id'])
    utils.info('The build container has stopped after {:.1f} seconds.'.format(time.time() - start_time))

    if exit_code == 0:
        utils.log('Build succeeded.')
    else:
        if utils.get_log_level() > 0:
            utils.log('Build script failed with exit code: {}.'.format(exit_code), err=True, fg='red', bold=True)
        else:
            utils.log('Build script failed with exit code: {}. Output follows:'.format(exit_code), err=True, fg='red', bold=True)
            utils.log(docker.logs(container['Id']).decode('utf-8').rstrip(), err=True, prefix=False)

    if config['keep_containers']:
        utils.info('Keeping build container (ID: {}).'.format(container['Id'][:12]))
    else:
        utils.debug('Removing build container.')
        docker.remove_container(container['Id'])
        utils.info('Removed build container.')

    remove_build_script(script_path)

    return exit_code == 0
Beispiel #7
0
def read(file_pointer):
    """Read the configuration file and parse the YAML contents."""
    utils.debug('Reading the configuration file.')

    try:
        config = yaml.load(file_pointer.read())
    except yaml.parser.ParserError as error:
        utils.log('The configuration file could not be parsed:', err=True, fg='red', bold=True)
        utils.log(error.context, err=True, prefix=False)
        utils.log(str(error.context_mark), err=True, prefix=False)
        utils.log(error.problem, err=True, prefix=False)
        utils.log(str(error.problem_mark), err=True, prefix=False)
        return False

    utils.info('The configuration file "{}" has been parsed.'.format(file_pointer.name))
    utils.debug('Parsed configuration:\n{}'.format(config))
    return config
Beispiel #8
0
def build(config):
    """Run the build part of the configuration."""
    utils.log('Building project.')

    script_path = create_build_script(config['build']['script'])
    script_container_path = '/{}'.format(path.basename(script_path))

    volumes = config['build'].get('volumes', dict())
    volumes[script_path] = script_container_path
    volume_list, binds = utils.convert_volumes_list(volumes)

    docker = utils.docker_client()
    container = docker.create_container(
        image=config['build']['image'],
        command=[script_container_path],
        volumes=volume_list,
        environment={str(k): str(v) for k, v in config.get('environment', dict()).items()},
    )

    if container['Warnings']:
        utils.log('We got a warning when creating the build container:', err=True, fg='red', bold=True)
        utils.log(str(container['Warnings']), err=True, prefix=False)
        remove_build_script(script_path)
        return False

    utils.debug('Starting build container.')
    start_time = time.time()
    docker.start(container['Id'], binds=binds)
    utils.info('Build container started.')

    if utils.get_log_level() > 0:
        for line in docker.logs(container['Id'], stream=True):
            utils.log(line.decode('utf-8').rstrip(), prefix=False)

    exit_code = docker.wait(container['Id'])
    utils.info('The build container has stopped after {:.1f} seconds.'.format(time.time() - start_time))

    if exit_code == 0:
        utils.log('Build succeeded.')
    else:
        if utils.get_log_level() > 0:
            utils.log('Build script failed with exit code: {}.'.format(exit_code), err=True, fg='red', bold=True)
        else:
            utils.log('Build script failed with exit code: {}. Output follows:'.format(exit_code), err=True, fg='red', bold=True)
            utils.log(docker.logs(container['Id']).decode('utf-8').rstrip(), err=True, prefix=False)

    if config['keep_containers']:
        utils.info('Keeping build container (ID: {}).'.format(container['Id'][:12]))
    else:
        utils.debug('Removing build container.')
        docker.remove_container(container['Id'])
        utils.info('Removed build container.')

    remove_build_script(script_path)

    return exit_code == 0
Beispiel #9
0
def verify(config):
    """Verify the configuration contain the necessary details."""
    errors = []

    if 'build' in config:
        for error in verify_build(config['build']):
            errors.append(error)
    else:
        utils.debug('No "build" section found in configuration.')

    for error in verify_package(config):
        errors.append(error)

    for section in {'build', 'package'}:
        for local_path, container_path in config.get(section, dict()).get(
                'volumes', dict()).items():
            if not path.exists(local_path) and not create_local_directory(
                    local_path):
                errors.append(
                    'The path "{}" specified as a {} volume does not exist on the local machine.'
                    .format(local_path, section))
            if not path.isabs(container_path):
                errors.append(
                    'The path "{}" specified as a {} volume has to be absolute.'
                    .format(container_path, section))

    if 'environment' in config and not isinstance(config['environment'], dict):
        errors.append(
            'The environment variables should be described as a dictionary.')

    if errors:
        utils.log(
            'The following errors were encountered while verifying the configuration:',
            err=True,
            fg='red',
            bold=True)
        for error in errors:
            utils.log('  - {}'.format(error), err=True, prefix=False)
        return False
    else:
        utils.info('The configuration has been verified without errors.')
        return True
Beispiel #10
0
def package(config):
    """Package a project of some kind to a package of some kind."""
    utils.log('Packaging project.')

    volumes = config['package'].get('volumes', dict())
    volume_list, binds = utils.convert_volumes_list(volumes)

    command = list(replace_envvars(config, map_to_fpm(config['package']['fpm'])))
    utils.debug('Command to be run in packaging container is: {}'.format(str(command)))

    docker = utils.docker_client()
    container = docker.create_container(
        image=config['package'].get('image', 'tenzer/fpm'),
        command=command,
        volumes=volume_list,
    )

    if container['Warnings']:
        utils.log('We got a warning when creating the packaging container:', err=True, fg='red', bold=True)
        utils.log(str(container['Warnings']), err=True, prefix=False)
        return False

    utils.debug('Starting packaging container.')
    start_time = time.time()
    docker.start(container['Id'], binds=binds)
    utils.info('Packaging container started.')

    if utils.get_log_level() > 0:
        for line in docker.logs(container['Id'], stream=True):
            utils.log(line.decode('utf-8').rstrip(), prefix=False)

    exit_code = docker.wait(container['Id'])
    utils.info('The packaging container has stopped after {:.1f} seconds.'.format(time.time() - start_time))

    if exit_code == 0:
        utils.log('Packaging succeeded.')
    else:
        if utils.get_log_level() > 0:
            utils.log('Packaging command failed with exit code: {}.'.format(exit_code), err=True, fg='red', bold=True)
        else:
            utils.log('Packaging command failed with exit code: {}. Output follows:'.format(exit_code), err=True, fg='red', bold=True)
            utils.log(docker.logs(container['Id']).decode('utf-8').rstrip(), err=True, prefix=False)

    if config['keep_containers']:
        utils.info('Keeping packaging container (ID: {}).'.format(container['Id'][:12]))
    else:
        utils.debug('Removing packaging container.')
        docker.remove_container(container['Id'])
        utils.info('Removed packaging container.')

    return exit_code == 0
Beispiel #11
0
def read(file_pointer):
    """Read the configuration file and parse the YAML contents."""
    utils.debug('Reading the configuration file.')

    try:
        config = yaml.load(file_pointer.read())
    except yaml.parser.ParserError as error:
        utils.log('The configuration file could not be parsed:',
                  err=True,
                  fg='red',
                  bold=True)
        utils.log(error.context, err=True, prefix=False)
        utils.log(str(error.context_mark), err=True, prefix=False)
        utils.log(error.problem, err=True, prefix=False)
        utils.log(str(error.problem_mark), err=True, prefix=False)
        return False

    utils.info('The configuration file "{}" has been parsed.'.format(
        file_pointer.name))
    utils.debug('Parsed configuration:\n{}'.format(config))
    return config
Beispiel #12
0
def main(context, config_file, verbose, debug, build_only, package_only,
         keep_containers):  # pylint: disable = too-many-arguments
    """Build a package."""
    if debug:
        utils.set_log_level(2)
    elif verbose:
        utils.set_log_level(1)

    if build_only and package_only:
        utils.log('--build-only and --package-only are mutually exclusive.',
                  err=True,
                  fg='red',
                  bold=True)
        context.exit(1)

    configuration = config.read(config_file)

    if not configuration:
        context.exit(1)

    if not config.verify(configuration):
        context.exit(1)

    configuration['keep_containers'] = keep_containers

    if 'build' not in configuration:
        utils.info('No build configuration provided, skipping build.')
    elif package_only:
        utils.info('--package-only provided, skipping build.')
    else:
        if not utils.pull_image(configuration['build']['image']):
            context.exit(1)

        if not build.build(configuration):
            context.exit(1)

    if not utils.pull_image(configuration['package'].get(
            'image', 'tenzer/fpm')):
        context.exit(1)

    if build_only:
        utils.info('--build-only provided, skipping packaging.')
    else:
        if not package.package(configuration):
            context.exit(1)

    utils.log('Done!')
    context.exit(0)
Beispiel #13
0
def package(config):
    """Package a project of some kind to a package of some kind."""
    utils.log('Packaging project.')

    volumes = config['package'].get('volumes', dict())
    binds = utils.convert_volumes_list(volumes)

    command = list(
        replace_envvars(config, map_to_fpm(config['package']['fpm'])))
    utils.debug('Command to be run in packaging container is: {}'.format(
        str(command)))

    docker = utils.docker_client()
    container = docker.create_container(
        image=config['package'].get('image', 'tenzer/fpm'),
        command=command,
        host_config=docker.create_host_config(binds=binds))

    if container['Warnings']:
        utils.log('We got a warning when creating the packaging container:',
                  err=True,
                  fg='red',
                  bold=True)
        utils.log(str(container['Warnings']), err=True, prefix=False)
        return False

    utils.debug('Starting packaging container.')
    start_time = time.time()
    docker.start(container['Id'])
    utils.info('Packaging container started.')

    if utils.get_log_level() > 0:
        for line in docker.logs(container['Id'], stream=True):
            utils.log(line.decode('utf-8').rstrip(), prefix=False)

    exit_code = docker.wait(container['Id'])
    utils.info(
        'The packaging container has stopped after {:.1f} seconds.'.format(
            time.time() - start_time))

    if exit_code == 0:
        utils.log('Packaging succeeded.')
    else:
        if utils.get_log_level() > 0:
            utils.log('Packaging command failed with exit code: {}.'.format(
                exit_code),
                      err=True,
                      fg='red',
                      bold=True)
        else:
            utils.log(
                'Packaging command failed with exit code: {}. Output follows:'.
                format(exit_code),
                err=True,
                fg='red',
                bold=True)
            utils.log(docker.logs(container['Id']).decode('utf-8').rstrip(),
                      err=True,
                      prefix=False)

    if config['keep_containers']:
        utils.info('Keeping packaging container (ID: {}).'.format(
            container['Id'][:12]))
    else:
        utils.debug('Removing packaging container.')
        docker.remove_container(container['Id'])
        utils.info('Removed packaging container.')

    return exit_code == 0
Beispiel #14
0
def remove_build_script(script_path):
    """Remove the temporary build script file."""
    utils.debug('Removing temporary build script at "{}".'.format(script_path))
    unlink(script_path)
    utils.info('Removed temporary build script.')
Beispiel #15
0
def remove_build_script(script_path):
    """Remove the temporary build script file."""
    utils.debug('Removing temporary build script at "{}".'.format(script_path))
    unlink(script_path)
    utils.info('Removed temporary build script.')