Commit graph

129 commits

Author SHA1 Message Date
Christian Kellner
554d8dc868 test/stages/groups: fix gid to be a number
According to the schema the "gid" option is a number not a string.
2020-05-06 15:42:23 +02:00
Christian Kellner
3f0ced73ed test/osbuild: use StageInfo to verify stage opts
Instead of manually loading the schema information of the stages
and assemblers, use the new osbuild.meta.StageInfo class.
2020-05-06 15:42:23 +02:00
Christian Kellner
e036f86119 test: fix freq, passno types everywhere
Both 'freq' and 'passno' org.osbuild.fstab2 stage options need to
be of type int, not string.
2020-05-06 15:42:23 +02:00
Christian Kellner
f81ae9c4c3 test/assemblers: omit None compression option
In the "test_tar" test the compression can be None, which in turn
will be serialized as "compression: null" in the options dict.
However, this is not a valid option according to the schema. The
schema could be adapted to allow for this but it is probably
better to just omit empty or null values.
2020-05-06 15:42:23 +02:00
Christian Kellner
bab736dd9a test/osbuild: add checks for validation
Add checks for the new validation API, specifically check that
errors in the main manifest, the stages and stage options,
assembler and assembler options. Also ensure that there are no
duplicated errors due to pipeline nesting (e.g. build pipelines).
2020-05-06 15:42:23 +02:00
David Rheinsberg
20cf5dba6a test: '{. -> ./mod}/test_util_osrelease.py'
Move the os-release tests to the module-level tests and align its
coding-style with the others.
2020-04-28 15:39:00 +02:00
David Rheinsberg
4493d057a2 osbuild: move osrelease parser into ./util
Reduce the clutter in our pipeline implementation and move the
os-release parser into ./osbuild/util/.
2020-04-28 15:39:00 +02:00
David Rheinsberg
7176f1e85a test: '{. -> ./mod}/test_util_linux.py'
Move the util-linux tests to the module-level tests and align its
implementation.
2020-04-28 15:39:00 +02:00
David Rheinsberg
ea7561329f test: '{. -> ./mod}/test_util_rmrf.py'
Move the rmrf tests to the module-level tests and align its
implementation with the others.
2020-04-28 15:39:00 +02:00
David Rheinsberg
1ec3e5a776 test: '{. -> ./mod}/test_util_jsoncomm.py'
Move the jsoncomm-test to the other module-level tests and align its
implementation with the others.
2020-04-28 15:39:00 +02:00
David Rheinsberg
aaa8854437 test: '{. -> ./run}/test_sources.py'
Move the runtime tests of source modules into the runtime-tests
subdirectory, so `make test-runtime` can pick them up.
2020-04-24 15:50:44 +02:00
David Rheinsberg
cecb27ac82 test: '{. -> ./mod}/test_util_ostree.py'
Move the OSTree utility tests into the module-unittests directory. Also
drop the `__main__` workaround while at it.
2020-04-24 15:50:44 +02:00
David Rheinsberg
6a7e811af2 test: '{. -> ./mod}/test_util_selinux.py'
Move the 'test_util_selinux.py' test into the module-unittest
subdirectory.

Drop the '__main__' hookup while at it. `python -m unittest --help`
explains how you can run individual tests.
2020-04-24 15:50:44 +02:00
David Rheinsberg
e5ff287f5a util/linux: add explicit FS_IMMUTABLE_FL helpers
The FS_IOC_{GET,SET}FLAGS ioctl numbers are not stable across different
architectures. Most of them use the asm-generic versions, but ALPHA and
SPARC in particular use completely different IOC number setups (see the
definition of _IOC, _IOR, _IOW, etc. in the kernel).

This commit moves the helpers for `FS_IMMUTABLE_FL` into
`osbuild/util/` and adds explicit tests. This will make sure that we
catch any ioctl mismatches as soon as possible when we run the osbuild
test-suite on other architectures. Until then, we will have to live with
this mismatch.
2020-04-21 14:46:02 +02:00
David Rheinsberg
2cc9160099 objectstore: extract remove_tree()
Move remove_tree() into its own module in `osbuild.util.rmrf`. This way
we can use it in other modules as well, without cross-referencing
internal helpers.
2020-04-21 14:46:02 +02:00
David Rheinsberg
6f8ba82fc6 util: add JSON Communication module
Add a new module that implements a simple JSON communication channel.
This is meant to replace all our hard-coded SOCK_DGRAM code that is
copied all over the place.

This is intentionally left simple. It only supports synchronous
operations, trivial JSON encoding and decoding, and uses a message-based
transport mode.
2020-04-21 13:47:38 +02:00
David Rheinsberg
870372055d test: drop unused arguments
Make pylint happy (and our code cleaner) and drop unused arguments (or
prefix with an underscore if we do not control the ABI).
2020-04-20 18:58:26 +02:00
David Rheinsberg
e73f88d398 test: make ast check pylint conforming
A few adjustments to the AST check:
  * Avoid local imports. Move the AST import to the head of the file.
    We will get warnings if it is unused, so it should not get stale
    there once we drop the AST hack.
  * Avoid `TODO:` since pylint will parse it (parsing comments, yay!)
  * Use `isinstance()` for type-checks.
2020-04-20 18:58:26 +02:00
David Rheinsberg
eb4d90cf94 test: avoid shadowing globals
Lets not use `input` as variable name, since it shadows the python
global `input`. Use a different variable name and make pylint happy.
2020-04-20 18:58:26 +02:00
David Rheinsberg
72be939521 test: fix minor pylint warnings
This fixes a bunch of minor pylint warnings:
  * Drop unused imports.
  * Fix "inline-JSON" formatting.
  * Fix space before/after brackets.
  * Use `_` for unused variables.
  * Break overlong lines.
  * Mark unittest as `no-self-use` if applicable.
  * Drop spurious newline at end of file.
2020-04-20 18:58:26 +02:00
David Rheinsberg
617ed6ed29 test: fix pylint coding-style warnings
This fixes 3 things:
  * Drop an unused argument from the http server handler.
  * Break an overlong `with:` statement.
  * Fix indentation where it is wrong.
2020-04-20 18:58:26 +02:00
David Rheinsberg
f50f9d912f test: fix import order
Python mandates global imports before local imports (pylint should warn
about this). Fix the test to import locals late.
2020-04-20 18:58:26 +02:00
David Rheinsberg
7d2e895d26 test: avoid import-aliasing for trivial case
This drops the `server` alias for `http.server`. There is only a single
caller, so lets just be explicit so the callsite is easier to
understand.

As a side effect, this unifies all the imports, no cherrypicking
anymore.
2020-04-20 18:58:26 +02:00
Tom Gundersen
c2243aee6a stage: add org.osbuild.first-boot
This stage runs a given command only on the first boot of the image,
useful for doing instantiation tasks that can only be done in the
target environment, or that should be done per-instance, rather
than per image.

Ideally we would use systemd's ConditionFirstBoot for this, but that
requires images to ship without an /etc/machine-id, and currently
we only support shipping images with an empty /etc/machine-id.
Changing this would mean dropping /etc/fstab in favor of mounting
the rootfs rw from the initrd. This is likely the right thing to
do regardless, but we would have to audit what other first-boot
services we would end up with pulling in in this case.

Instead we introduce our own flag file /etc/osbuild-first-boot,
and use ConditionPathExists.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2020-04-15 16:07:34 +02:00
Christian Kellner
50beb4ffb5 util: add selinux config file related helpers
Add a helper, `parse_config`, to parse a selinux configuration file,
see selinux(8), and return a dictionary containing the configuration
data in key, value pairs. This, in turn, can be fed into the other
helper method, `config_get_policy`, to get the effective policy or
`None` if SELinux is disabled or the policy type is not configured.
Add a new test suite that checks the basic functionality of the
helpers above.
2020-04-15 15:39:45 +02:00
Christian Kellner
47412e1bb7 util: new ostree module with TreeFile helper
When using rpm-ostree compose, a Treefile[1] controls various
aspects of its behaviour. Since rpm-ostree will, at least in
the beginning, be used to post-process and committing the tree
add a helper class to ease the creation of correct Treefiles.
The docstring of the Treefile contains the information in which
phases ('install', 'postprocess', 'commit') the option is used,
as of rpm-ostree commit 1cf0d557ae8059e689b1fed670022727e9842288

Add basic checks for the ostree.Treefile helper. Some of the
tests require rpm-ostree to be installed.

[1] https://rpm-ostree.readthedocs.io/en/stable/manual/treefile/
2020-04-15 15:39:45 +02:00
Tom Gundersen
afd94b1017 test/pipelines: drop sources.json
This was unused, as the test pipelines now contains the sources
inline.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2020-04-15 15:29:52 +02:00
Tom Gundersen
9d79d5fcc3 stages/grub2: default to disabling legacy support
For the sake of backwards compatibility, legacy support was enabled
by default. Flip this around, so that leaving the parameter out
means disabling it.

This is more intuitive, and will pave the way for dropping support
for the value being a bool in the future.

`osbuild-composer` always passes the argumnet explicitly, though
still always as a boolean.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2020-04-14 23:47:08 +02:00
David Rheinsberg
71e481f0d9 test: prune cache after each stage-test
Make sure we prune the caches after each stage-test to keep our disk
footprint small. This does considerably increase build-times since we
no longer share cached entries. However, the current CI builds simply
run out of disk-space.

Once we use separate output-directories we will be able to drop the
automatic checkpointing from the tests, and thus effectively get the
same behavior. Until then, lets prune the caches explicitly.
2020-04-07 18:20:42 +02:00
Christian Kellner
13432e275c test/objecstore: check cleanup of immutable files
Make sure that the Object can cleanup after itself in the case
that its tree contains immutable files.
2020-03-30 23:58:33 +02:00
David Rheinsberg
4587d97522 test/boot: convert -accel ... -> -M accel=... for qemu
Recent qemu version will warn with our current code:

    qemu-system-x86_64: -accel kvm:hvf:tcg: Don't use ':' with -accel,
                        use -M accel=... for now instead

Since this might result in hard-errors, lets just follow the advice and
use the `-M` switch.
2020-03-27 08:28:14 +01:00
Christian Kellner
457f21a336 objectstore: add HostTree class to access host fs
Simple new object that should expose the root file system with the
same API as `objectstore.Object` but as read-only. This means that
the `read` call works exactly as for `Object` but `write` raises
an exception.
Add tests to specifically check the read-only properties.
2020-03-07 17:13:21 +01:00
Christian Kellner
01efa9f817 tests/objectstore: check tracking of objects
Ensure all objects are properly cleaned up when ObjectStore is
used as a context manager and the context is exited.
2020-03-07 17:13:21 +01:00
Lars Karlitski
c09b7b007e test: remove README.md
It only contained references to a Vagrant workflow that doesn't exist
anymore.
2020-03-06 11:57:17 +01:00
Tom Gundersen
26f5135a5f tests: move from dnf- to rpm-based pipelines
This should produce the same images (except for no more dnf metadata),
but avoids depsolving (and network access) from stages.

The following helper script around osbuild-composer's dnf-json was
used for the translation:

```

import json
import os
import subprocess
import sys

def fetch_repos(sources, repo):
    if isinstance(repo, str):
        repo = sources["org.osbuild.dnf"]["repos"][repo]

    return repo, repo["gpgkey"]

def convert_stage(sources, stage, cachedir):
    gpgkeys = []
    repos = []
    for repoid, repo in enumerate(stage["options"]["repos"]):
        repo, gpgkey = fetch_repos(sources, repo)
        repo["id"] = repoid
        repo["name"] = f"repo-{repoid}"
        repos.append(repo)
        gpgkeys.append(gpgkey)

    arguments = {}
    arguments["cachedir"] = cachedir
    arguments["module_platform_id"] = stage["options"]["module_platform_id"]
    arguments["package-specs"] = stage["options"]["packages"]
    arguments["exclude-specs"] = stage["options"].get("exclude_packages", [])
    arguments["repos"] = repos

    call = {}
    call["command"] = "depsolve"
    call["arguments"] = arguments

    r = subprocess.run(["./dnf-json"],
                   input=json.dumps(call),
                   stdout=subprocess.PIPE,
                   encoding="utf-8",
                   check=True)
    pkgs = json.loads(r.stdout)["dependencies"]

    packages = []
    urls = {}
    for p in pkgs:
        packages.append(p["checksum"])
        urls[p["checksum"]] = p["remote_location"]

    options = {}
    options["gpgkeys"] = gpgkeys
    options["packages"] = packages

    stage["name"] = "org.osbuild.rpm"
    stage["options"] = options

    return urls

def convert_pipeline(sources, pipeline, cachedir):
    urls = {}
    if "build" in pipeline:
        u = convert_pipeline(sources, pipeline["build"]["pipeline"], cachedir)
        urls = {**urls, **u}
    for stage in pipeline["stages"]:
        if stage["name"] == "org.osbuild.dnf":
            u = convert_stage(sources, stage, cachedir)
            urls = {**urls, **u}
    return urls

manifest = json.load(sys.stdin)
urls = convert_pipeline(manifest["sources"], manifest["pipeline"], f"{os.getcwd()}/dnf-cache")
sources = { "org.osbuild.files": { "urls": urls }}
json.dump({"sources": sources, "pipeline": manifest["pipeline"]}, sys.stdout)
                                                                                                                                                                                                                            75,9          Bot
```

Signed-off-by: Tom Gundersen <teg@jklm.no>
2020-03-03 00:05:26 +01:00
Tom Gundersen
320c08b76c tests/dnf: use baseurl over metalink
Using a metalink resolves to a specific mirror at runtime, and
downloads each rpm from that repository.

We want to move to using the org.osbuild.files source, which means
that we must save the url to each rpm in the source definition, which
will be determined by which mirror is used to generate the config.

If we use metalinks to generate the source configuration, the mirror
used will be arbitrary. Instead, we want to pick the best mirror
explicitly, ideally in a way that is independent of the location
depsolving happens in (which will be different from the location
the rpms are downloaded to).

We can choose explicitly by passing baseurl rather than metalink
to dnf, so move in that direction now by replacing all metalinks
by baseurls in our dnf configuration.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2020-03-03 00:05:26 +01:00
Tom Gundersen
4e9f5d4473 tests/pipelines: embed all sources with their respective pipelines
We now support sources and pipelines being passed to osbuild as one.

This will make the transformation from dnf to rpm stage simpler, as
the source object will then be different for each stage, so having
a shared one as now would be cumbersome.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2020-03-03 00:05:26 +01:00
Tom Gundersen
ff8fda9e53 tests/dnf: always specify platform_module_id
As long as this matches the build environment, this does not make
a differenece, but let us not depend on this.

This will be useful when automatically transforming dnf to rpm
pipelines, as the platform_module_id is needed as input to
osbuild-composer's dnf-json tool.

Performed using this script:

```

cat $1 | jq '(.stages[]? | select(.name == "org.osbuild.dnf") | .options.module_platform_id) |= . + "platform:f30"'  | sponge $1
cat $1 | jq '(.build.pipeline.stages[]? | select(.name == "org.osbuild.dnf") | .options.module_platform_id) |= . + "platform:f30"'  | sponge $1
```

Signed-off-by: Tom Gundersen <teg@jklm.no>
2020-03-03 00:05:26 +01:00
David Rheinsberg
88f9915577 test: add 2 more os-release tests
This adds two more os-release tests. One contains an empty os-release
file. We verify it is correctly parsed and ends up with the default
value.

The second one is the official Arch Linux os-release file. We verify
that we correctly end up with the rolling-release name.
2020-02-29 12:35:19 +01:00
David Rheinsberg
53415a3cbc pipeline: detect_os() -> describe_os()
Rename the function to `describe_os()`. We do no actual detection, nor
verification here. That is, the return value of this function is in no
way guaranteed to be a valid runner. That is, error-handling needs to
be done in the caller. Make this clear by renaming the function.

Note: Currently, in case no runner exists for the OS, we end up with:

          execv(...) failed: No such file or directory

      This needs to be fixed in the future.
2020-02-29 12:35:19 +01:00
David Rheinsberg
cd07d588fc pipeline: fix detect_os() default values
The keys in `/etc/os-release` are not mandatory. Make sure we use their
default values (defined in the man-page) if missing.
2020-02-29 12:35:19 +01:00
Christian Kellner
4b790ac284 objectstore: use a context also for Object.write
Reading from an `Object` via `read` already uses a context manager
to manage the read-only bind mount and also maintain a count of
currently active readers. With this an attempt to start a new
`write` operation while readers were active can be detected and
an exception is throw. Since `write` was not introducing a context
the inverted situation, i.e. reads while a write is ongoing, was
not possible to detect.
This commit therefore introduces a context also for `.write` so
that we can enforce the policy to have either many readers but no
writers, or just one writer and no readers.
A bind mount is also used for write (in read-write mode) to hide
the internal path of the tree.
2020-02-29 01:14:24 +01:00
Christian Kellner
2fa9f86112 tests: basic check for base of Object
Check that setting the base of an Object will initialize the tree
with the contents of that base on the first write.
2020-02-28 16:11:49 +01:00
Christian Kellner
4ba91f2f4c tests: check operation modes for Object
Verify that `Object` can have multiple readers but as long as at
least one reader is active, no one can write to it. Also checks
that after `Object` has left the context it is not writable anymore.
2020-02-28 16:11:49 +01:00
Christian Kellner
a2cbed0ceb tests: copy on write checks for objectstore.Object
Verify the copy on write semantics of `objectstore.Object`, i.e.
content will only be copied at the moment a client wants to write
to `Object`. This also checks that `Object.base` works.

Modify the CI to execute the unit tests in a privileged container
because `Object.read()` works internally by bind mounting a path.
The mount operation needs at least CAP_SYS_ADMIN and overwriting
the file permissions CAP_DAC_OVERRIDE.
2020-02-28 16:11:49 +01:00
Christian Kellner
6b167abf5d tests: extend basic object store checks
Add basic unit test for the `contains` and `resolve_ref` methods.
2020-02-28 16:11:49 +01:00
Christian Kellner
42a365d12f osbuild: no auto commit of the last stage
Do not automatically commit the last stage of the pipeline to the
store. The last stage is most likely not what should be cached,
because it will contain all the individual customization and thus
be very likely different for different users. Instead, the dnf or
rpm stages have a higher chance of being the same and thus are
better candidates for caching.
Technically this change is done via two big changes that build
upon new features introduces in the previous commits, most notably
the copy on write semantics of Object and that input/output is
being done via `objectstore.Object` instead of plain paths. The
first of the two big changes is  to create one new `Object` at
the beginning of `pipeline.run` and use that, in write mode via
`Object.write` across invocations of `stage.run` calls, with
checkpoints being created after each stage on demand.
The very same `Object` is then used in read mode via `Object.read`
as the input tree for the Assembler. After the assembler is done
the resulting image/tree is manually committed to the store.
The other big change is to remove the `ObjectStore.commit` call
from the `ObjectStore.new` method and thus the automatic commit
after the last stage is gone.
NB: since the build tree is being retrieved in `get_buildtree`
from the store, a checkpoint for the last stage of the build
pipeline is forced for now. Future commits will refactor will
do away with that forced commit as well.
Change osbuildtest.TestCase to always create a checkpoint at
the final tree (the last stage of the pipeline), since tests
need it to check the tree contents.
2020-02-28 16:11:49 +01:00
Christian Kellner
6a2a7d99f7 objectstore: unify commit and snapshot code paths
As a result of the previous commits that implement copy on write
semantics, `commit` can now be used to create snapshots. Whenever
an Object is committed, its tree is moved to the store and it is
being reset, i.e. a new clean workdir is created and the old one
discarded. The moved tree is then set as the base of the reset
Object. On the next call to `write` the moved tree will be copied
over and forms the basis of the Object again. Should nobody want
to write to Object after the snapshot, i.e. the `commit`, no copy
will be made.
NB: snapshots/commits will act now act as synchronization points:
if a object with the same treesum, i.e. the very same content
already exists, the move (i.e. `store_tree`) will gracefully fail
and the existing content will be set as the base for Object.
2020-02-28 16:11:49 +01:00
Christian Kellner
39213b7f44 objectstore: copy on write semantics for Object
Since Object knows its base now, the initialization of the tree
with the content of its base can be delayed until the moment
someone wants to actually modify the tree, thus implementing
copy on write semantics. For this a new `write` method is added
that will initialize the base and return the writable tree. It
should be used instead of `path` whenever the a client wants to
write to the tree of the Object.
Adapt the pipeline and the tests to use the new `write` method
in all the appropriate places.
NB: since the intention can not be inferred when using `path`
directly, the Object is still being initialized there.
2020-02-28 16:11:49 +01:00
Christian Kellner
25b3807a5b objectstore: snapshot takes Object not path
Refactor the `ObjectStore.snapshot` method to take an `Object` not
a plain filesystem tree, so the latter is more encapsulated from
the ObjectStore user (e.g. the pipeline) and prepares a unified
code-path for `snapshot` and `commit` in the future.
2020-02-28 16:11:49 +01:00