Commit graph

400 commits

Author SHA1 Message Date
Christian Kellner
2d6fa9564c samples/f30-s390x: use new stlye sources for dnf
Instead of directly encoding the repo configuration make use of the
new sources api. The corresponding entry in samples/sources.json
which is referenced here ("sha256:450d4c0…") was added with the
previous commit ("92cfc57d720…").

    "it's the future! 🛸" - Lars Karlitski
2020-01-13 20:05:10 +01:00
Christian Kellner
8d7cd7c871 samples/sources: add f30 on s390x dnf repo
Add the repo source for the org.osbuild.dnf stage for fedora 30 on
s390x.
2020-01-13 20:05:10 +01:00
Christian Kellner
d53787be66 samples/f30-s390x.json: example for s390x
Bare bones example for image creation on s390x with MBR partition
layout and zipl bootloader. The kernel command line is configured
via the new org.osbuild.kernel-cmdline stage which needs to be run
before the dnf stage.
2020-01-13 20:05:10 +01:00
Christian Kellner
49e1b91d47 assembler/qemu: support for zipl bootloader
Support the s390x bootloader zipl (z Initial Program Loader). We
supply the parameters for the kernel+initrd as well es the target,
i.e. the boot partition where the bootmap is creating, the device,
here called 'targetbase', to install the bootloader on, including
parameters describing the device (type, blocksize) and also the
offset of the partition containing the target from the start of
device (in sectors).
The kernel and initrd are found via the bootloader entry, ignoring
the rescue kernel.
Since zipl needs the device as well as access to the boot partition
the image is bound to a loopback device. Also keep the filesystem
tree mounted during the execution of the zipl installation.
2020-01-13 20:05:10 +01:00
Christian Kellner
a32c30d06c test: add simple check for kernel-cmdline stage
Add a stage test to check that the new kopts stage is creating the
target file /etc/kernel/cmdline with the right content. Since tree
diff currently seems to lack support for content hashing new files
we work around this by creating first an empty /etc/kernel/cmdline
file and then get a content diff with the desired options set.
2020-01-13 20:05:10 +01:00
Christian Kellner
7a8c76cb1c stages/kopts: new stage to set the kernel cmdline
The canonical way to set the kernel commandline, which is used by
the kernel post install scripts, see kernel-install(8), is the
file /etc/kernel/cmdline, or in the case this does not exist,
/proc/cmdline. The new stages offers a way to write this file in a
more "type-safe" way, by providing explicit options for certain
well known params (for now only `root_fs_uuid`). Additional params
are specified via `kernel_opts`. This follows the grub2 stage name
convention.
2020-01-13 20:05:10 +01:00
Christian Kellner
40096e189b test: new test to check zipl stage
The zipl stage is a fairly simple stage that just creates a file
in /etc called zipl.conf with a single configurable option, which
is called `timeout`. Check the file gets properly created with
the desired hash and verify that setting the timeout works.
2020-01-13 20:05:10 +01:00
Christian Kellner
fc9108e16e stage/zipl: new stage to configure zipl
Write a default config for the z Initial Program Loader to work
correctly. Parameters taken from anaconda[1].

[1] 2e793483b4/pyanaconda/bootloader/zipl.py (L132)
2020-01-13 20:05:10 +01:00
Christian Kellner
64addbe2d2 buildroot: allow creating device nodes on s390x
The z Initial Program Loader (zipl) when creating the bootmap in
bootmap_creat (src/zipl/bootmap.c) wants to create a device node
via misc_temp_dev (bootmap_create:1141) for the device that it
is installing the bootloader to[1]. Currently access to loopback
devices is allowed from within the container (it is used to mount
the image), but only read/write access. On s390x also allow the
creation of device nodes, so zipl can do its work and install
the bootloader stages on the "disk".

[1] zipl source at commit dcce14923c3e9615df53773d1d8a3a22cbb23b96
2020-01-13 20:05:10 +01:00
Christian Kellner
bf41326ac6 remoteloop: don't use O_DIRECT on s390x
Using O_DIRECT to open the image partition and then using that fd
for the backing of the loopback device will break the mounting of
the formatted partition, i.e mount will fail with:

  mount: /tmp/looptest-6qrtkp5e/mountpoint-root: wrong fs type,
  bad option, bad superblock on /dev/loop0, missing codepage or
  helper program, or other error.

Reproducible with the follow small-ish python script, executed via
'env PYTHONPATH=$(pwd) python3 looptest.py':

---- 8< ---- 8< ---- [ looptest.py ] ---- 8< ---- 8< ----

import contextlib
import json
import os
import subprocess
import stat
import tempfile

from osbuild import loop

@contextlib.contextmanager
def mount(source, dest):
    subprocess.run(["mount", source, dest], check=True)
    try:
        yield dest
    finally:
        subprocess.run(["umount", "-R", dest], check=True)

@contextlib.contextmanager
def os_open(path, flags):
    fd = os.open(path, flags)
    try:
        yield fd
    finally:
        os.close(fd)

def main():
    size = 512 * 1024 * 1024
    ptuuid = "0x14fc63d2"

    with contextlib.ExitStack() as cm:
        tmpdir = cm.enter_context(tempfile.TemporaryDirectory(prefix="looptest-"))
        print(f"Temporary directory at {tmpdir}")

        devdir = os.path.join(tmpdir, "dev")
        os.makedirs(devdir, exist_ok=True)
        dir_fd = cm.enter_context(os_open(devdir, os.O_DIRECTORY))

        image = os.path.join(tmpdir, "image")
        subprocess.run(["truncate", "--size", str(size), image], check=True)
        table = f"label: mbr\nlabel-id: {ptuuid}\nbootable, type=83"
        subprocess.run(["sfdisk", image], input=table, encoding='utf-8',
                       check=True)
        # read it back
        r = subprocess.run(["sfdisk", "--json", image],
                           stdout=subprocess.PIPE,
                           encoding='utf-8', check=True)
        table = json.loads(r.stdout)["partitiontable"]
        partitions = table["partitions"]
        start = partitions[0]["start"] * 512
        size = partitions[0]["size"] * 512

        # fails here with os.O_DIRECT
        image_fd = cm.enter_context(os_open(image, os.O_RDWR | os.O_DIRECT))

        control = loop.LoopControl()
        minor = control.get_unbound()
        lo = loop.Loop(minor)
        lo.set_fd(image_fd)
        lo.set_status(offset=start, sizelimit=size, autoclear=True)
        lo.mknod(dir_fd)
        loopdev = f"/dev/loop{minor}"

        # loopdev = os.path.join(devdir, lo.devname)
        # os.chmod(loopdev, os.stat(loopdev).st_mode | stat.S_IRGRP)

        subprocess.run(["ls", "-la", f"{devdir}"], check=True)
        subprocess.run(["mkfs.ext4", loopdev],
                       input="y", encoding='utf-8', check=True)

        subprocess.run(["blkid", loopdev], check=True)

        mountpoint = os.path.join(tmpdir, "mountpoint-root")
        os.makedirs(mountpoint, exist_ok=True)
        cm.enter_context(mount(loopdev, mountpoint))
        subprocess.run(["ls", "-la", tmpdir], check=True)
        subprocess.run(["ls", "-la", mountpoint], check=True)
        subprocess.run(["mount"], check=True)

if __name__ == '__main__':
    main()
2020-01-13 20:05:10 +01:00
Christian Kellner
65996d1370 assembler/qemu: declare bootloader options
Include the `bootloader` options in the STAGE_OPTS json schema.
Commit 8fcf7d5c4… introduce the `bootloader` option but the
corresponding schema entry was omitted.
2020-01-13 20:05:10 +01:00
Martin Sehnoutka
6a14ba40f7 Create Fedora 31 and Fedora 32 runners 2020-01-10 18:27:55 +01:00
Christian Kellner
b79a215659 README: add information about 'sources'
Update the command line options help text as well as the sample
command line to build the `base-qcow2.json` to include the new
sources command line option.
2020-01-10 18:26:12 +01:00
Christian Kellner
20399a57ee samples: add hybrid boot example
Add a example demonstrating hybrid boot. The main ingredients are:
 - grub2 stage has `uefi` and `legacy` options
 - GPT partition layout
 - BIOS boot partition so that core image for grub2 legacy can
   be stored (small, 1MB, type 21686148-6449-6E6F-744E-656564454649)
 - ESP partition so UEFI can load grub*.efi (via the shim)
2020-01-10 18:25:35 +01:00
Christian Kellner
4804632268 stages/grub2: support for hybrid booting
In the case that the image should support booting via EFI *and*
legacy grub, i.e. hybrid booting, the canonical grub config is
stored in /boot/grub2 just as for normal legacy booting. The
config file for efi grub is a very small one that will just look
for the partition containing the /boot/grub2/grub.cfg file and use
then set the prefix accordingly and load that file. In the hybrid
case grubenv file also will just be located in /boot/grub2 and
not in the ESP therefore the symlink that was created by the
package needs to be removed.
2020-01-10 18:25:35 +01:00
Christian Kellner
b50193f289 assemblers/qemu: fix backward compat for grub2
With the introduction of the `bootloader` option, grub2 legacy
installation setting changed. Before, grub2 legacy installation
was dependent on the partition scheme, i.e. only when dos/mbr
layout was used grub2 got installed. After the change the default
is to install it unless `bootloader.type" is explicitly set, even
if the partition layout is GPT. But a legacy grub2 installation
on GPT requires a BIOS boot partition, so the new default is not
right for the case of pure (non-hyrid) UEFI images.
Therefore revert to the old behavior of only defaulting to grub2
legacy if the option is not explicitly set *and* the partition
layout is "dos"/"mbr".
Adapt the f30-qcow2-gpt sample, which is non-uefi grub2 legacy
but with GPT and a bios boot partition, to explicitly request
the grub2 bootloader.
2020-01-10 15:48:27 +01:00
Christian Kellner
42a4176a9f stages/grub2: fix auto bool to platform conversion
In the case that legacy is of type bool it is automatically converted
to the platform string ("i386-legacy"). This is mainly done to keep
backwards comparability as it was just a boolean before. But the auto
conversion did not take the actual *value* of the boolean into account
meaning "legacy: False" would be turned into "legacy: i386-pc" and
thus effectively changing the value from False to True.
2020-01-10 15:48:27 +01:00
Lars Karlitski
3a73d589d2 stages/dnf: don't ignore unavailable repositories
dnf skips unavailable repositories by default, which only leads to
harder understand errors later. Configure it to fail when any of the
passed repositories cannot be reached.
2020-01-09 23:55:43 +01:00
Lars Karlitski
84a948bcd7 stages/dnf: don't print repository data
It may contain secrets now, which we don't want in the logs.
2020-01-09 23:55:43 +01:00
Lars Karlitski
59ffebaff0 stages,sources/dnf: allow passing certificate data
Add support for dnf's sslcacert, sslclientcert, and sslclientkey
options. The latter two are passed as secrets (clientcert as well
because it might be a pem file that also includes the private key).

Sources run on the host, so their options may contain paths to the host
file system. Make use of that by accepting only paths in those options,
because it allows using tools to deal with certificate files.

Also make sure that the dnf source only returns options it knows about.
2020-01-09 23:55:43 +01:00
Lars Karlitski
12b5c6aaa4 sources: bump maximum message size to 64k
These messages contain certificate data, which is quite large.

We should probably use streaming sockets in the future.
2020-01-09 23:55:43 +01:00
Lars Karlitski
e123715bc6 osbuild: introduce secrets
Add a new command line option `--secrets`, which accepts a JSON file
that is structured similarly to a source file. It is should contain data
that is necessary to fetch content, but shouldn't appear in any logs.
2020-01-09 23:55:43 +01:00
Lars Karlitski
02ad4e3810 sources: fail gracefully when a source returns invalid JSON
Include the actual output of the source to help debugging.
2020-01-09 23:55:43 +01:00
Lars Karlitski
ef7f17d95b osbuild.spec: install sources directory
Missed it in 510e2b1e94.
2020-01-09 23:55:20 +01:00
Christian Kellner
ba0acf7ff8 samples: example for legacy boot but gpt partition
An example demonstrating the use of the BIOS boot partition to
enable legacy grub2 boot (i.e. non-efi boot) with a GPT partition
layout.
2020-01-09 01:43:56 +01:00
Christian Kellner
9e929d3db6 assembler/qemu: refactor grub2 boot image patching
As noted in earlier commits the grub2 boot image needs to be patched
to contain the position of the grub2 core. By default, the location
in the boot image is hard-coded to be the mbr gap (sector 1) but for
GPT partition schemes a separate BIOS boot partition is used that is
located at a "random" location. Refactor the code to generalize the
boot image patching, where the default mbr gap location is just a
special case of the general.
2020-01-09 01:43:56 +01:00
Christian Kellner
814cb4eb80 assembler/qemu: gpt support for grub legacy
The GRUB2 bootloader in legacy mode, i.e. non-EFI mode, consists of
several stages. The fist one place in the in the Master Boot Record
of the disk will load and execute the next, second stage, consisting
of core modules and the grub kernel. The first bit is also known as
'boot' and the second as 'core'. When the 'MBR' partition layout is
being used, there is a gap between the Master Boot Record (MBR) and
the first partition (for historical and performance reasons). The
core image normally is placed into this gap (call the MBR gap).
When the partition layout is 'gpt' there is no standard gap that can
be used, instead a special partition ("BIOS boot" [1]) needs to be
created that can store the grub2 core image. Additionally, the 'boot'
image need to modified to point the sector of that partition. The
core image itself also needs to be modified with the information of
the location its own second sector. The location of the pointers
were taken from the grub2 source ([2] at commit [3]). For the 'boot'
image it is 'GRUB_BOOT_MACHINE_KERNEL_SECTOR' (0x5c) from 'pc/boot.h'
and for the core image "0x200 - GRUB_BOOT_MACHINE_LIST_SIZE (12)" to
be found in 'pc/diskboot.S'.

[1] https://en.wikipedia.org/wiki/BIOS_boot_partition
[2] https://github.com/rhboot/grub2
[3] 2a2e10c1b39672de3d5da037a50d5c371f49b40d
2020-01-09 01:43:56 +01:00
Christian Kellner
c83019a264 assembler/qemu: extract grub2 boot image writing
Extract the small piece of code that writes the grub2's boot image,
i.e. the first stage of the bootloader that will in turn jump to
the second stage. Currently the position of the core is hard-coded
to be the MBR gap, i.e. the gap between the MBR and the start of
the first partition. This is not a necessity, e.g. when using a
dedicated BIOS boot partition on GPT partition layouts. This re-
factoring should make it easier to add code dealing with such
situations.
2020-01-09 01:43:56 +01:00
Lars Karlitski
b9b2f99123 osbuild: create API sockets in the thread they're used in
This might (hopefully) fix a race in destructing the asyncio.EventLoop
that's used in all API classes, which leads to warnings about unhandled
exceptions on CI.

This also puts their creation closer to where the client-side sockets
are created.
2019-12-25 17:48:26 +01:00
Christian Kellner
57b2c1e12d samples: include ppc64le sample
Include a example of a ppc64le (open firmware) pipeline based on
f30-s390x. It includes changes to the grub2 stage (selecting the
platform), the partition table contains the PReP partition needed
to store the grub2 core image.
2019-12-24 15:42:24 +01:00
Christian Kellner
1da71ebbb4 assembler/qemu: support for ppc64le (open firmware)
Introduce support for ppc64le (Open Firmware). The main difference
to x86 legacy, i.e. non-efi, is that no stage 1 is required because
the core image is stored on a special 'PReP' partition, which must
be marked as bootable. The firmware then looks for that partition
and directly loads the core from there and executes it.
Introduce a `platform` parameter for the grub installer code which
controls various platform depended aspects, including a) the path
for the modules, b) what modules are compiled into the core, c) if
the boot image is written to the MBR and 4) where to write the core
image, i.e. mbr-gap or PReP partition.
2019-12-24 15:42:24 +01:00
Christian Kellner
ccf9994ba6 assembler/qemu: extra func for writing grub core
Extract the function that writes the grub2 core to the image file.
The only supported location currently is the MBR gap, which is the
gap between the Master Boot Record and the first partition, which
for historical and performance reasons was aligned to a certain
sector (used to be 64 but now is even larger with 2048). In the
future other locations for the grub2 core will be supported such
as the PReP partition (ppc64le) or bios-boot (GPT hybrid booting).
2019-12-24 15:42:24 +01:00
Christian Kellner
8fcf7d5c45 assembler/qemu: explicit bootloader selection
Make the bootloader selection explicit by introducing a new option
called `bootloader`, which is an object, containing the `type` and
options belonging to the bootloader. For now only boot-loader that
is supported is "grub2".
2019-12-24 15:42:24 +01:00
Christian Kellner
e12667914a assembler/qemu: dynamically determine grub2 prefix
Instead of hard-coding "msdos1", determine this partition id
dynamically based on the partition table type and the index
of the partition that contains /boot/grub2, which normally is
either a separate boot partition or the root partition. In
order to be able to do so, set the index of each Partition
when the partition information is read back via `sfdisk`.
NB: partition indexes start at 1 for grub2.
2019-12-24 15:42:24 +01:00
Christian Kellner
33078bf1df assembler/qemu: we want the boot fs type for grub2
The filesystem module that grub2 needs to have in the core image
is the filesystem containing the grub modules, specifically the
"normal.mod", as well as the grub configuration. In the standard
case, which is also what osbuild uses, this is /boot/grub2; thus
we actually do want the filesystem containing that directory and
its type not the root filesystem.
2019-12-24 15:42:24 +01:00
Christian Kellner
c57da5722f assembler/qemu: clarify module usage for grub2
Explain the concept and reason behind the grub2 core as well as the
details behind the selection of the core  modules that get included.
Also elaborate a bit on the MBR gap. For more details about this see
https://en.wikipedia.org/wiki/GNU_GRUB#Version_2_(GRUB_2)
NB: This commit also changes the order of the grub modules, which in
turn changes the layout of the core.img and thus the hash value used
in the test; adapt those value to reflect the changed core.img.
2019-12-24 15:42:24 +01:00
Christian Kellner
828b568734 stage/grub2: support for non i386-pc platforms
Change the `legacy` option (keeping compatibility) from a boolean
to a string, specifying the target platform the legacy modules
are being installed for.
2019-12-24 15:42:24 +01:00
Lars Karlitski
08b7a1a6b5 stages/dnf: don't show repo id in checksum error
repoid was based on index in the "repos" array from options. Now that we
fetch repository configuration from a source, the order might be
different.
2019-12-23 01:12:38 +01:00
Lars Karlitski
510e2b1e94 osbuild: introduce sources
Pipelines encode which source content they need in the form of
repository metadata checksums (or rpm checksums). In addition, they
encode where they fetch that source content from in the form of URLs.
This is overly specific and doesn't have to be in the pipeline's hash:
the checksum is enough to specify an image.

In practice, this precluded using alternative ways of getting at source
packages, such as local mirrors, which could speed up development.

Introduce a new osbuild API: sources. With it, a stage can query for a
way to fetch source content based on checksums.

The first such source is `org.osbuild.dnf`, which returns repository
configuration for a metadata checksum. Note that the dnf stage continues
to verify that the content it received matches the checksum it expects.

Sources are implemented as programs, living in a `sources` directory.
They are run on the host (i.e., uncontained) right now. Each source gets
passed options, which are taken from a new command line argument to
osbuild, and an array of checksums for which to return content.

This API is only available to stages right now.
2019-12-23 01:12:38 +01:00
Lars Karlitski
b81a11800d test: merge duplicate dnf stages 2019-12-23 01:12:38 +01:00
Tom Gundersen
1f7766596f stages/dnf: log more clearly in case the repo metadata changes
This is an expected error case, so we should not assert, but log
and return failure. In the future we should probably also return
the error as structured data.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2019-12-18 22:18:33 +01:00
Christian Kellner
f67a649805 assembler/qemu: support partition names (gpt)
The GPT (GUID Partition Table) standard for partition layout supports
giving partition a name in the Partition object as well as in the
option for the qemu stage when specifying the partition layout.
2019-12-18 20:45:54 +01:00
Christian Kellner
1ea04d803f assembler/qemu: mkfs_for_type → Filesystem.make_at
Make mkfs_for_type a member of Filesystem (as 'make').
2019-12-18 20:45:54 +01:00
Christian Kellner
dc25fb3e42 assembler/qemu: helper to root fs partition
Introduce a method on the PartitionTable that returns the partition
containing the root filesystem. NB: this does not have to be the
first partition (which could be the EFI partition, or something
else), so we have to iterate through the partitions until we find
it.
2019-12-18 20:45:54 +01:00
Christian Kellner
5ee68aef30 assembler/qemu: Partition{Table} & Filesystem objs
Instead of having dictionaries representing the partition table,
partitions and filesystems together with some functions operating
on them, have proper python objects with methods. In the future
these objects could be extract and properly tested as well.
2019-12-18 20:45:54 +01:00
Christian Kellner
83c3f9608d samples: add a simple error example
Uses the org.osbuild.error stage to test error behavior and handling.
2019-12-18 12:45:59 +01:00
Christian Kellner
6333d1c3bd stage/error: new simple stage always erroring out
A simple stage like 'noop' that will return with `returncode` or
255 if nothing is specified. Like 'noop' it might be useful for
testing, debugging, and wasting time.
2019-12-18 12:45:59 +01:00
Christian Kellner
ede3f6baeb pipeline: proper object cleanup on errors
The recent changes removed the {Assembler,Stage}Failed exceptions,
which includes them being thrown from Stage.run and Assembler.run.
Instead result dictionaries are returned even on errors. But the
object store, used as a context manager, relies on exceptions to
detect the error case and thus needs them to cleanup the temporary
objects. Without those exceptions the temporary objects end up in
the store even when the sage or assembler failed.
Restore the old behavior by throwing a generic BuildError exception
from the Stage and Assembler, which will be caught directly in the
pipeline and converted to a result dict.
2019-12-18 12:45:59 +01:00
Lars Karlitski
9350e868a7 test: don't capture stderr when running osbuild
stdout is expected to be JSON.
2019-12-18 11:12:37 +01:00
Lars Karlitski
f09a0c2a69 test/assemblers: don't delete output in the store
To fix running the test multiple times with OSBUILD_TEST_STORE.
2019-12-18 11:12:37 +01:00