Example #1
0
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
Example #2
0
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
Example #3
0
    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]
Example #4
0
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)
Example #8
0
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)
Example #9
0
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()
Example #10
0
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
Example #11
0
def test_builddir_failure_on_nonexisting_path():
    with pytest.raises(FileNotFoundError, match="does not exist"):
        BuildDir(Path("some_dir"), "x86_64")