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', }, }, }
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".', ]
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.', ]
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'), ]}, }
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, }