Example #1
0
def test_valid_procfile():
    lines = loads('\n'.join([
        'web: bundle exec rails server -p $PORT',
        'worker: env QUEUE=* bundle exec rake resque:work',
        'urgentworker: env QUEUE=urgent FOO=meh bundle exec rake resque:work',
    ]))
    assert lines == {
        'web': {
            'cmd': 'bundle exec rails server -p $PORT',
            'env': {},
        },
        'worker': {
            'cmd': 'bundle exec rake resque:work',
            'env': {
                'QUEUE': '*',
            },
        },
        'urgentworker': {
            'cmd': 'bundle exec rake resque:work',
            'env': {
                'QUEUE': 'urgent',
                'FOO': 'meh',
            },
        },
    }
Example #2
0
def test_duplicate_variable():
    with pytest.raises(ValueError) as error:
        print(loads('\n'.join([
            'web: bundle exec rails server -p $PORT',
            'worker: env QUEUE=* QUEUE=urgent bundle exec rake resque:work',
        ])))
    assert error.value.args[0] == [
        'Line 2: duplicate variable "QUEUE" for process type "worker".',
    ]
Example #3
0
def test_duplicate_process_types():
    with pytest.raises(ValueError) as error:
        print(loads('\n'.join([
            'web: bundle exec rails server -p $PORT',
            'web: bundle exec rake resque:work',
        ])))
    assert error.value.args[0] == [
        'Line 2: duplicate process type "web": already appears on line 1.',
    ]
Example #4
0
def test_duplicate_variable():
    with pytest.raises(ValueError) as error:
        print(loads('\n'.join([
            'web: bundle exec rails server -p $PORT',
            'worker: env QUEUE=* QUEUE=urgent bundle exec rake resque:work',
        ])))
    assert error.value.args[0] == [
        'Line 2: duplicate variable "QUEUE" for process type "worker".',
    ]
Example #5
0
def test_duplicate_process_types():
    with pytest.raises(ValueError) as error:
        print(loads('\n'.join([
            'web: bundle exec rails server -p $PORT',
            'web: bundle exec rake resque:work',
        ])))
    assert error.value.args[0] == [
        'Line 2: duplicate process type "web": already appears on line 1.',
    ]
Example #6
0
def test_valid_procfile():
    lines = loads('\n'.join([
        'web: bundle exec rails server -p $PORT',
        'worker: env QUEUE=* bundle exec rake resque:work',
        'urgentworker: env QUEUE=urgent FOO=meh bundle exec rake resque:work',
    ]))
    assert lines == {
        'web': {'cmd': 'bundle exec rails server -p $PORT', 'env': []},
        'worker': {'cmd': 'bundle exec rake resque:work', 'env': [
            ('QUEUE', '*'),
        ]},
        'urgentworker': {'cmd': 'bundle exec rake resque:work', 'env': [
            ('QUEUE', 'urgent'),
            ('FOO', 'meh'),
        ]},
    }
Example #7
0
def build_image(tarfileobj, image, registry, registry_username,
                registry_password, docker_url, docker_secure, docker_ca):
    with ExitStack() as stack:
        temp_dir = stack.enter_context(TemporaryDirectory())
        try:
            tar_ball = stack.enter_context(
                TarFile(fileobj=tarfileobj, mode='r'))
        except Exception as exc:
            raise BuildError(f'{exc}')
        for tarinfo in tar_ball:
            if os.path.normpath(tarinfo.name).startswith((os.sep, '/', '..')):
                raise BuildError(
                    ('refusing to touch sketchy tarball, '
                     'no relative paths outside of root directory allowed '
                     f'{tarinfo.name} exits top level directory'))
            if not (tarinfo.isfile() or tarinfo.isdir()):
                raise BuildError(
                    ('refusing to touch sketchy tarball, '
                     'only regular files and directories allowed '
                     f'{tarinfo.name} is not a regular file or directory'))
        try:
            try:
                tar_ball.getmember('./Dockerfile.cabotage')
            except KeyError:
                tar_ball.getmember('./Dockerfile')
        except KeyError:
            raise BuildError(
                ('must include a Dockerfile.cabotage or Dockerfile'
                 'in top level of archive'))
        try:
            try:
                tar_ball.getmember('./Procfile.cabotage')
            except KeyError:
                tar_ball.getmember('./Procfile')
        except KeyError:
            raise BuildError('must include a Procfile.cabotage or Procfile '
                             'in top level of archive')
        tar_ball.extractall(path=temp_dir, numeric_owner=False)
        if os.path.exists(os.path.join(temp_dir, 'Procfile.cabotage')):
            shutil.copy(
                os.path.join(temp_dir, 'Procfile.cabotage'),
                os.path.join(temp_dir, 'Procfile'),
            )
        with open(os.path.join(temp_dir, 'Procfile'), 'rU') as img_procfile:
            procfile_body = img_procfile.read()
        if os.path.exists(os.path.join(temp_dir, 'Dockerfile.cabotage')):
            shutil.copy(
                os.path.join(temp_dir, 'Dockerfile.cabotage'),
                os.path.join(temp_dir, 'Dockerfile'),
            )
        with open(os.path.join(temp_dir, 'Dockerfile'),
                  'rU') as img_dockerfile:
            dockerfile_body = img_dockerfile.read()
            dockerfile_object = DockerfileParser(temp_dir)
            dockerfile_env_vars = list(dockerfile_object.envs.keys())
        image.dockerfile = dockerfile_body
        image.procfile = procfile_body
        db.session.commit()
        try:
            processes = procfile.loads(procfile_body)
        except ValueError as exc:
            raise BuildError(f'error parsing Procfile: {exc}')
        tls_config = False
        if docker_secure:
            tls_config = docker.tls.TLSConfig(client_cert=None,
                                              ca_cert=docker_ca,
                                              verify=True,
                                              ssl_version='PROTOCOL_TLSv1_2')
        client = docker.DockerClient(base_url=docker_url, tls=tls_config)
        response = client.api.build(
            path=temp_dir,
            tag=f'{registry}/{image.repository_name}:image-{image.version}',
            rm=True,
            forcerm=True,
            dockerfile="Dockerfile",
            buildargs=image.buildargs(config_writer),
        )
        build_errored = False
        log_lines = []
        for chunk in response:
            for line in chunk.split(b'\r\n'):
                if line:
                    payload = json.loads(line.decode())
                    stream = payload.get('stream')
                    status = payload.get('status')
                    aux = payload.get('aux')
                    error = payload.get('error')
                    if stream:
                        log_lines.append(stream)
                    if status:
                        if payload.get('progressDetail'):
                            continue
                        if payload.get("id"):
                            log_lines.append(
                                f'{payload.get("id")}: {status}\n')
                        else:
                            log_lines.append(f'{status}\n')
                    if error is not None:
                        errorDetail = payload.get('errorDetail', {})
                        message = errorDetail.get('message', 'unknown error')
                        build_errored = (f'Error building image: {message}')
                    if aux:
                        if 'ID' in aux:
                            built_id = aux['ID']
                            log_lines.append(
                                f'Built Image with ID: {built_id}\n')
        image.image_build_log = ''.join(log_lines)
        db.session.commit()
        if build_errored:
            raise BuildError(build_errored)
        built_image = client.images.get(
            f'{registry}/{image.repository_name}:image-{image.version}')
        client.login(
            username=registry_username,
            password=registry_password,
            registry=registry,
            reauth=True,
        )
        client.images.push(f'{registry}/{image.repository_name}',
                           f'image-{image.version}')
        pushed_image = client.images.get(
            f'{registry}/{image.repository_name}:image-{image.version}')
        return {
            'image_id': pushed_image.id,
            'processes': processes,
            'dockerfile': dockerfile_body,
            'procfile': procfile_body,
            'dockerfile_env_vars': dockerfile_env_vars,
        }