def test_builddir_get_parsed_dockerfile_with_parent_env( inspection_data: ImageInspectionData, expected_envs, tmpdir): dir_path = Path(tmpdir) dir_path.joinpath(DOCKERFILE_FILENAME).write_text( "FROM base-image\nENV HOME=$HOME", "utf-8") build_dir = BuildDir(dir_path, "x86_64") parsed_df = build_dir.dockerfile_with_parent_env(inspection_data) assert expected_envs == parsed_df.envs
def test_flatpak_create_dockerfile(workflow, source_dir, config_name, override_base_image, breakage): config = CONFIGS[config_name] modules = None if breakage == 'no_modules': modules = [] expected_exception = "a module is required for Flatpaks" elif breakage == 'multiple_modules': modules = ['eog:f28:20170629213428', 'flatpak-common:f28:123456'] expected_exception = None # Just a warning else: assert breakage is None expected_exception = None data = yaml.safe_load(config['container_yaml']) if override_base_image is not None: data['flatpak']['base_image'] = override_base_image if modules is not None: data['compose']['modules'] = modules container_yaml = yaml.dump(data) platforms = ["x86_64", "s390x"] mock_workflow(workflow, source_dir, container_yaml, platforms) base_image = "registry.fedoraproject.org/fedora:latest" reactor_config = { 'version': 1, 'flatpak': {'base_image': base_image}, 'source_registry': {'url': 'source_registry'}, } runner = (MockEnv(workflow) .for_plugin(FlatpakCreateDockerfilePlugin.key) .set_reactor_config(reactor_config) .create_runner()) if expected_exception: with pytest.raises(PluginFailedException) as ex: runner.run() assert expected_exception in str(ex.value) else: runner.run() flatpak_util = FlatpakUtil(workflow_config=None, source_config=workflow.source.config) source_spec = flatpak_util.get_flatpak_source_spec() assert source_spec == config['source_spec'] expect_base_image = override_base_image if override_base_image else base_image for platform in platforms: build_dir = BuildDir(workflow.build_dir.path / platform, platform) df = build_dir.dockerfile_path.read_text("utf-8") assert "FROM " + expect_base_image in df assert 'name="{}"'.format(config['name']) in df assert 'com.redhat.component="{}"'.format(config['component']) in df assert "RUN rm -f /etc/yum.repos.d/*" in df assert "ADD atomic-reactor-repos/* /etc/yum.repos.d/" in df
def render_help_file(self, build_dir: BuildDir) -> List[Path]: """Update the help.md file in the build directory and use it to generate a man file.""" dockerfile = build_dir.dockerfile_with_parent_env( # platform should not matter, we only care about the component and maintainer labels self.workflow.imageutil.base_image_inspect()) labels = Labels(dockerfile.labels) try: _, name = labels.get_name_and_value(Labels.LABEL_TYPE_NAME) except KeyError: name = '' maintainer = dockerfile.labels.get('maintainer', '') help_path = build_dir.path / self.help_file start_time = get_pipeline_run_start_time( self.workflow.osbs, self.workflow.pipeline_run_name) with open(help_path, 'r+') as help_file: lines = help_file.readlines() if not lines[0].startswith("% "): lines.insert(0, "%% %s (1) Container Image Pages\n" % name) lines.insert(1, "%% %s\n" % maintainer) lines.insert(2, "%% %s\n" % start_time.strftime("%B %-d, %Y")) help_file.seek(0) help_file.truncate() help_file.writelines(lines) self.log.info( "added metadata to %s for generating nicer manpages", help_path) man_path = build_dir.path / self.man_filename go_md2man_cmd = ['go-md2man', f'-in={help_path}', f'-out={man_path}'] try: check_output(go_md2man_cmd, stderr=STDOUT) except OSError as e: if e.errno == errno.ENOENT: raise RuntimeError( "Help file is available, but go-md2man is not present in a buildroot" ) from e raise except CalledProcessError as e: raise RuntimeError( "Error running %s: %s, exit code: %s, output: '%s'" % (e.cmd, e, e.returncode, e.output)) from e if not man_path.exists(): raise RuntimeError( "go-md2man run complete, but man file is not found") # We modified one file and created the other, let's copy both to all per-platform dirs return [help_path, man_path]
def test_builddir_dockerfile_not_support_linked_dockerfile(tmpdir): some_dir = Path(tmpdir) dockerfile = some_dir / DOCKERFILE_FILENAME dockerfile.touch() # link the Dockerfile build_dir = Path(tmpdir, "build_x86_64") build_dir.mkdir() build_dir.joinpath(DOCKERFILE_FILENAME).symlink_to(dockerfile) with pytest.raises(DockerfileNotExist, match="Dockerfile is linked from"): print(BuildDir(build_dir, "x86_64").dockerfile_path)
def build_flatpak_image(self, source, build_dir: BuildDir) -> Dict[str, Any]: builder = FlatpakBuilder(source, build_dir.path, 'var/tmp/flatpak-build', parse_manifest=parse_rpm_output, flatpak_metadata=self.flatpak_metadata) df_labels = build_dir.dockerfile_with_parent_env( self.workflow.imageutil.base_image_inspect() ).labels builder.add_labels(df_labels) tmp_dir = tempfile.mkdtemp(dir=build_dir.path) image_filesystem = self.workflow.imageutil.extract_filesystem_layer( str(build_dir.exported_squashed_image), str(tmp_dir)) build_dir.exported_squashed_image.unlink() filesystem_path = os.path.join(tmp_dir, image_filesystem) with open(filesystem_path, 'rb') as f: # this part is 'not ideal' but this function seems to be a prerequisite # for building flatpak image since it does the setup for it flatpak_filesystem, flatpak_manifest = builder._export_from_stream(f) os.remove(filesystem_path) self.log.info('filesystem tarfile written to %s', flatpak_filesystem) image_rpm_components = builder.get_components(flatpak_manifest) ref_name, outfile, outfile_tarred = builder.build_container(flatpak_filesystem) os.remove(outfile_tarred) metadata = get_exported_image_metadata(outfile, IMAGE_TYPE_OCI) metadata['ref_name'] = ref_name cmd = ['skopeo', 'copy', 'oci:{path}:{ref_name}'.format(**metadata), '--format=v2s2', 'docker-archive:{}'.format(str(build_dir.exported_squashed_image))] try: retries.run_cmd(cmd) except subprocess.CalledProcessError as e: self.log.error("skopeo copy failed with output:\n%s", e.output) raise RuntimeError("skopeo copy failed with output:\n{}".format(e.output)) from e self.log.info('OCI image is available as %s', outfile) shutil.rmtree(tmp_dir) self.workflow.data.image_components[build_dir.platform] = image_rpm_components return metadata
def build_flatpak_image(self, source, build_dir: BuildDir) -> Dict[str, Any]: builder = FlatpakBuilder(source, build_dir.path, 'var/tmp/flatpak-build', parse_manifest=parse_rpm_output, flatpak_metadata=self.flatpak_metadata) df_labels = build_dir.dockerfile_with_parent_env( self.workflow.imageutil.base_image_inspect()).labels builder.add_labels(df_labels) tmp_dir = tempfile.mkdtemp(dir=build_dir.path) image_filesystem = self.workflow.imageutil.extract_filesystem_layer( str(build_dir.exported_squashed_image), str(tmp_dir)) filesystem_path = os.path.join(tmp_dir, image_filesystem) with open(filesystem_path, 'rb') as f: # this part is 'not ideal' but this function seems to be a prerequisite # for building flatpak image since it does the setup for it flatpak_filesystem, flatpak_manifest = builder._export_from_stream( f) self.log.info('filesystem tarfile written to %s', flatpak_filesystem) image_rpm_components = builder.get_components(flatpak_manifest) ref_name, outfile, _ = builder.build_container(flatpak_filesystem) metadata = get_exported_image_metadata(outfile, IMAGE_TYPE_OCI) metadata['ref_name'] = ref_name self.log.info('OCI image is available as %s', outfile) shutil.rmtree(tmp_dir) return {'metadata': metadata, 'components': image_rpm_components}
def add_labels_to_df(self, build_dir: BuildDir) -> None: """Add labels to a platform-specific Dockerfile.""" base_image_labels: Dict[str, str] base_image_inspect = self.workflow.imageutil.base_image_inspect( build_dir.platform) dockerfile = build_dir.dockerfile_with_parent_env(base_image_inspect) df_images = self.workflow.data.dockerfile_images if df_images.custom_base_image or df_images.base_from_scratch: base_image_labels = {} else: try: config = base_image_inspect[INSPECT_CONFIG] except KeyError as exc: message = "base image was not inspected" self.log.error(message) raise RuntimeError(message) from exc else: base_image_labels = config["Labels"] or {} add_labels = self.labels.copy() generated_labels = self.generate_auto_labels(build_dir.platform) add_labels.update(generated_labels) # changing dockerfile.labels writes out modified Dockerfile - err on # the safe side and make a copy alias_labels = self.add_aliases(base_image_labels.copy(), dockerfile.labels.copy(), add_labels.copy()) add_labels.update(alias_labels) if self.info_url_format: info_url = self.get_info_url(base_image_labels.copy(), dockerfile.labels.copy(), add_labels.copy()) add_labels["url"] = info_url labels = [] for key, value in add_labels.items(): if key not in dockerfile.labels or dockerfile.labels[key] != value: if key in self.dont_overwrite_if_in_dockerfile and key in dockerfile.labels: self.log.info( "denying overwrite of label %r, using from Dockerfile", key) elif (key in base_image_labels and key in self.dont_overwrite and key not in dockerfile.labels): self.log.info( "denying overwrite of label %r, using from baseimage", key) else: label = label_to_string(key, value) self.log.info("setting label %r", label) labels.append(label) if labels: label_line = f"LABEL {' '.join(labels)}\n" # put labels at the end of dockerfile (since they change metadata and do not interact # with FS, this should cause no harm) dockerfile.lines = dockerfile.lines + ["\n", label_line] self.add_release_env_var(dockerfile)
def test_builddir_get_parsed_dockerfile(tmpdir): dir_path = Path(tmpdir) dir_path.joinpath(DOCKERFILE_FILENAME).write_text("FROM fedora:35", "utf-8") build_dir = BuildDir(dir_path, "x86_64") assert isinstance(build_dir.dockerfile, DockerfileParser)
def test_builddir_dockerfile_path_returns_absolute_path(tmpdir): dir_path = Path(tmpdir) dir_path.joinpath(DOCKERFILE_FILENAME).touch() build_dir = BuildDir(dir_path, "x86_64") assert build_dir.dockerfile_path.is_absolute()
def test_builddir_dockerfile_path(tmpdir): dir_path = Path(tmpdir) dir_path.joinpath(DOCKERFILE_FILENAME).touch() build_dir = BuildDir(dir_path, "x86_64") assert Path(tmpdir.join(DOCKERFILE_FILENAME)) == build_dir.dockerfile_path
def test_builddir_failure_on_nonexisting_path(): with pytest.raises(FileNotFoundError, match="does not exist"): BuildDir(Path("some_dir"), "x86_64")