Exemple #1
0
def build_rpm(package_dir: Path, arch: str, rpm: Rpm,
              gpg_signing_key: str) -> Path:
    'Returns the filename of the built RPM.'
    with temp_dir(dir=package_dir) as td, tempfile.NamedTemporaryFile() as tf, \
            Path.resource(__package__, 'busybox', exe=True) as busybox_path:
        tf.write(rpm.spec(busybox_path).encode())
        tf.flush()

        work_dir = Path(generate_work_dir())

        format_kwargs = {
            "quoted_arch": shlex.quote(arch),
            "quoted_buildroot": Path(work_dir / 'build').shell_quote(),
            "quoted_home": Path(work_dir / 'home').shell_quote(),
            "quoted_spec_file": shlex.quote(tf.name),
            "quoted_work_dir": work_dir.shell_quote(),
            # We get the uid of the current user so that we can chown the
            # work_dir *inside* the running container.  The nspawn'd build
            # appliance container needs to run as root so that it can mkdir
            # the `work_dir` which exists at /.  If we don't chown the
            # resulting tree that `rpmbuild` creates the rename would would
            # fail.
            "current_uid": os.getuid(),
        }

        opts = new_nspawn_opts(
            cmd=[
                'sh',
                '-uec',
                '''\
                /usr/bin/rpmbuild \
                -bb \
                --target {quoted_arch} \
                --buildroot {quoted_buildroot} \
                {quoted_spec_file} \
                && chown -R {current_uid} {quoted_work_dir} \
                '''.format(**format_kwargs),
            ],
            layer=_build_appliance(),
            bindmount_ro=[(tf.name, tf.name), (busybox_path, busybox_path)],
            bindmount_rw=[(td, work_dir)],
            user=pwd.getpwnam('root'),
            setenv=['HOME={quoted_home}'.format(**format_kwargs)],
        )
        run_non_booted_nspawn(opts, PopenArgs())

        # `rpmbuild` has a non-configurable output layout, so
        # we'll move the resulting rpm into our package dir.
        rpms_dir = td / 'home/rpmbuild/RPMS' / arch
        rpm_name, = rpms_dir.listdir()
        os.rename(rpms_dir / rpm_name, package_dir / rpm_name)
        sign_rpm(package_dir / rpm_name, gpg_signing_key)
        return rpm_name
Exemple #2
0
    def spec(self, busybox_path: Path) -> str:
        format_kwargs = {
            **self._asdict(),
            'quoted_contents':
            shlex.quote(f'{self.name} {self.version} {self.release}' if self.
                        override_contents is None else self.override_contents),
            'quoted_busybox_path':
            busybox_path.shell_quote(),
        }

        common_spec = textwrap.dedent('''\
        Summary: The "{name}" package.
        Name: rpm-test-{name}
        Version: {version}
        Release: {release}
        Provides: virtual-{name}
        License: MIT
        Group: Facebook/Script
        Vendor: Facebook, Inc.
        Packager: [email protected]

        %description
        %install
        mkdir -p "$RPM_BUILD_ROOT"/rpm_test
        echo {quoted_contents} > "$RPM_BUILD_ROOT"/rpm_test/{name}.txt
        mkdir -p "$RPM_BUILD_ROOT"/bin
        ''').format(**format_kwargs)

        return common_spec + textwrap.dedent(('''\
            %files
            /rpm_test/{name}.txt
        ''') if not self.test_post_install else (
            '''\
            cp {quoted_busybox_path} "$RPM_BUILD_ROOT"/bin/sh
            %post
            '''

            # yum-dnf-from-snapshot prepares /dev in a subtle way to protect
            # host system from side-effects of rpm post-install scripts.
            # The command below lets us test that /dev/null is prepared
            # properly: if "echo > /dev/null" fails, tests will catch the
            # absence of post.txt
            '''\
            echo > /dev/null && echo 'stuff' > \
              "$RPM_BUILD_ROOT"/rpm_test/post.txt
            %files
            /bin/sh
            /rpm_test/{name}.txt
        ''')).format(**format_kwargs)