Skip to content

Add cosa import#4164

Merged
jlebon merged 4 commits into
coreos:mainfrom
PeaceRebel:pr/cmd-import
Jul 18, 2025
Merged

Add cosa import#4164
jlebon merged 4 commits into
coreos:mainfrom
PeaceRebel:pr/cmd-import

Conversation

@PeaceRebel
Copy link
Copy Markdown
Contributor

This command takes as argument a containers-transport(5)-style pullspec and creates a new cosa build dir from it. It essentially bridges the gap between coreos/fedora-coreos-config#3348 and the rest of the cosa pipeline.

co-authored by: Bipin B Narayan bbnaraya@redhat.com

@PeaceRebel PeaceRebel self-assigned this Jun 25, 2025
@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented Jun 25, 2025

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@PeaceRebel
Copy link
Copy Markdown
Contributor Author

PeaceRebel commented Jun 25, 2025

@jlebon I have pushed a very dirty draft here (using a new PR as I'm getting permission error when I try to push to your branch).

I'm getting error on cosa buildextend-qemu if this is the only build available and I'm looking at it. If there is a existing local build, I have to run the cosa buildextend-qemu twice to get a a bootable build.

I have set the ostree-content-checksum to a dummy value now. This should either come as a label for the container image or we should change it to be an optional field (this is set as a required field in the schema validation).

@jlebon
Copy link
Copy Markdown
Member

jlebon commented Jun 26, 2025

I'm getting error on cosa buildextend-qemu if this is the only build available and I'm looking at it. If there is a existing local build, I have to run the cosa buildextend-qemu twice to get a a bootable build.

What error are you seeing?

I have set the ostree-content-checksum to a dummy value now. This should either come as a label for the container image or we should change it to be an optional field (this is set as a required field in the schema validation).

Yeah indeed, we should make it optional.

@PeaceRebel
Copy link
Copy Markdown
Contributor Author

What error are you seeing?

This is the error if the import build is the only one available locally.

ERROR Opening ostree repository at '/srv/tmp/repo/../../cache/repo-build': opening repo: opendir(objects): No such file or directory
Traceback (most recent call last):
  File "<string>", line 10, in <module>
    cmdlib.import_ostree_commit(workdir, builddir, buildmeta, extract_json=('0' == '1'))
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/coreos-assembler/cosalib/cmdlib.py", line 337, in import_ostree_commit
    subprocess.check_call(['sudo', 'ostree', 'container', 'import', '--repo', build_repo,
    ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                           '--write-ref', buildmeta['buildid'],
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                           'ostree-unverified-image:oci-archive:' + tarfile])
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.13/subprocess.py", line 419, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['sudo', 'ostree', 'container', 'import', '--repo', '/srv/tmp/repo/../../cache/repo-build', '--write-ref', '43.20250614.91.0', 'ostree-unverified-image:oci-archive:/srv/builds/43.20250614.91.0/x86_64/fedora-coreos-43.20250614.91.0-ostree.x86_64.ociarchive']' returned non-zero exit status 1.
failed to execute cmd-build: exit status 1

Comment thread cmd/coreos-assembler.go Outdated
@PeaceRebel
Copy link
Copy Markdown
Contributor Author

What error are you seeing?

This is the error if the import build is the only one available locally.

ERROR Opening ostree repository at '/srv/tmp/repo/../../cache/repo-build': opening repo: opendir(objects): No such file or directory
Traceback (most recent call last):
  File "<string>", line 10, in <module>
    cmdlib.import_ostree_commit(workdir, builddir, buildmeta, extract_json=('0' == '1'))
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/coreos-assembler/cosalib/cmdlib.py", line 337, in import_ostree_commit
    subprocess.check_call(['sudo', 'ostree', 'container', 'import', '--repo', build_repo,
    ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                           '--write-ref', buildmeta['buildid'],
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                           'ostree-unverified-image:oci-archive:' + tarfile])
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.13/subprocess.py", line 419, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['sudo', 'ostree', 'container', 'import', '--repo', '/srv/tmp/repo/../../cache/repo-build', '--write-ref', '43.20250614.91.0', 'ostree-unverified-image:oci-archive:/srv/builds/43.20250614.91.0/x86_64/fedora-coreos-43.20250614.91.0-ostree.x86_64.ociarchive']' returned non-zero exit status 1.
failed to execute cmd-build: exit status 1

I found problem and fixed it now .

@jlebon
Copy link
Copy Markdown
Member

jlebon commented Jun 27, 2025

I found problem and fixed it now .

For the record, the problem here was that tmp/repo was not initialized.

This is normally handled by prepare_build(), which is called by cosa build. There might be other things of that kind from prepare_build() that are still needed for e.g. a direct cosa buildextend-qemu to work.

It'd probably make sense to factor out those idempotent bits from prepare_build() into a separate function, and then have prepare_build() call that but also we can call it from cmd-osbuild

@PeaceRebel
Copy link
Copy Markdown
Contributor Author

There might be other things of that kind from prepare_build() that are still needed for e.g. a direct cosa buildextend-qemu to work.

I'll take a look at that function, I skimmed through it to once. But with these changes I got a qemu image build, no errors.

@PeaceRebel PeaceRebel force-pushed the pr/cmd-import branch 2 times, most recently from cd0f927 to b9d955d Compare July 1, 2025 17:47
@PeaceRebel PeaceRebel changed the title WIP: Add cosa import Add cosa import Jul 1, 2025
@PeaceRebel PeaceRebel marked this pull request as ready for review July 1, 2025 17:49
@PeaceRebel PeaceRebel requested a review from jlebon July 1, 2025 17:49
@PeaceRebel
Copy link
Copy Markdown
Contributor Author

@jlebon, I have removed the ref key from meta.json created here. I'm not sure what it's used for. It wasn't in the file for rhcos.

Copy link
Copy Markdown
Member

@jlebon jlebon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this! Did a first pass here but would like to test drive it as well. Did you sanity-checking that the kola tests pass with a QEMU image built from an imported build? There is cosa diff also that would be useful here, though that doesn't also cover e.g. the QEMU image. Though in practice, the input to the QEMU build is the container image, so a cosa diff at the ostree level should cover these. Still, worth also doing a cosa buildextend-live, and cosa diff with the various --live options too to sanity-check they're identical, and then do a kola testiso run.

Comment thread src/cmd-import Outdated
Comment thread src/cmd-import Outdated
Comment thread src/cmd-import Outdated
Comment thread src/cmd-import Outdated
Comment thread src/cmd-import Outdated
Comment thread src/cmd-import Outdated
Comment thread src/cmd-import Outdated
Comment thread src/cmd-import Outdated
@jlebon
Copy link
Copy Markdown
Member

jlebon commented Jul 3, 2025

Make sure when testing this that you're testing with an FCOS built using https://github.com/coreos/fedora-coreos-config/blob/testing-devel/Containerfile and not what we currently push to https://quay.io/repository/fedora/fedora-coreos?tab=tags.

@PeaceRebel
Copy link
Copy Markdown
Contributor Author

Make sure when testing this that you're testing with an FCOS built using https://github.com/coreos/fedora-coreos-config/blob/testing-devel/Containerfile and not what we currently push to https://quay.io/repository/fedora/fedora-coreos?tab=tags.

I was testing it with images from quay.io. I will check this.

@cverna
Copy link
Copy Markdown
Member

cverna commented Jul 4, 2025

Testing the gemini review integration

/gemini review

@joelcapitao
Copy link
Copy Markdown
Member

You can now remove the commit updating the lockfiles (see #4171)

@PeaceRebel
Copy link
Copy Markdown
Contributor Author

@jlebon I got qemu with the recent changes. Some of your PR comments are yet to be addressed. I'll be working on it soon.

@PeaceRebel
Copy link
Copy Markdown
Contributor Author

osbuild patch is merged. osbuild/osbuild#2148

@jlebon
Copy link
Copy Markdown
Member

jlebon commented Jul 16, 2025

Comments addressed! Also re-added the osbuild patch since it's merged upstream.

Copy link
Copy Markdown
Member

@dustymabe dustymabe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this! Added some comments.

Comment thread src/cmd-osbuild Outdated
Comment thread src/cmd-import Outdated
Comment thread src/cmd-import
Comment thread src/cmd-import Outdated
Comment thread src/cmd-import Outdated
Comment thread src/cmd-import Outdated
PeaceRebel and others added 4 commits July 16, 2025 16:07
This command takes as argument a `containers-transport(5)`-style pullspec
and creates a new cosa build dir from it. It essentially bridges the gap
between coreos/fedora-coreos-config#3348 and the
rest of the cosa pipeline.

Co-authored-by: Jonathan Lebon <jonathan@jlebon.com>
There's a massive gotcha with bash, which is that even with `set -e`,
if you're capturing output from a function, e.g. `x=$(myfunc)`, `set
-e` will not be turned on in that function. That made an error in that
function much harder to diagnose because the command _kept going_ even
though the function failed (and then the overall operation failed later
on on a much less clear error).

There's a bash option to enable this (`shopt inherit_errexit`), but I
think actually that in general we shouldn't be capturing output from
non-trivial bash functions. It's hard in bash to ensure clean stdout
output and the longer a function gets, the harder it is to keep this up
(notice e.g. how that function needs to be careful to `echo` everything
to stderr).

In this case, it's easy to rework the flow here to avoid this, so let's
do that instead.
We now keep blob refs in the tmp repo to make importing OCI images more
efficient by avoiding having to re-import identical layers.

But then we also need to prune them. Do this.

The semantic here is simple: we prune any blob ref not tied to one of
the kept builds.
Treefile.json from rpm-ostree doesn't contain `name` in
the `metadata` field from images built using podman build.
@jlebon
Copy link
Copy Markdown
Member

jlebon commented Jul 16, 2025

OK, addressed review comments! I ended up reworking this quite a bit in the process of wanting to clean up the data/metadata flow through the various functions. It looks very different, but it's really just shifting all the same pieces around to make it easier to follow.

Also fixed a minor bug in the process (we were missing a size key for the ostree artifact).

@PeaceRebel
Copy link
Copy Markdown
Contributor Author

OK, addressed review comments! I ended up reworking this quite a bit in the process of wanting to clean up the data/metadata flow through the various functions. It looks very different, but it's really just shifting all the same pieces around to make it easier to follow.

Also fixed a minor bug in the process (we were missing a size key for the ostree artifact).

Thanks.

Copy link
Copy Markdown
Member

@jbtrystram jbtrystram left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superficial LGTM as it touches some part i don't have experience with.
Thanks, that's nice !

Are we planning to have a cosa wrapper to do the container build using versionary to have a nicer build id like 20250717.dev.0 then import it ?

@jlebon
Copy link
Copy Markdown
Member

jlebon commented Jul 17, 2025

So one bug here is that ostree-commit is not reproducible. That means that if you cosa import and then wipe tmp/repo and then e.g. cosa buildextend-qemu, when it tries to re-import the OCI archive, it might end up with a different digest and that throws things off.

Opened bootc-dev/bootc#1421 to fix this.

Copy link
Copy Markdown
Member

@dustymabe dustymabe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

I made some comments here but we can either disregard them or merge them in a later PR if you don't want to wait for a CI cycle on this.

Comment thread src/cosalib/cmdlib.py
Comment on lines +337 to +338
if is_oci_imported:
import_oci_archive(tmpdir, tarfile, buildmeta['buildid'])
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this part is still tripping me up a bit and I think it's the naming of things.

For example, we're calling checking is_oci_imported here and then calling import_oci_archive, but isn't the elif/else below also importing a .ociarchive file?

Maybe if we named the function import_container_built_ociarchive it would make more sense, because basically the different here that I see is in this case we are importing an archive that was created via podman build and in the other cases we are importing an archive that was generated by rpm-ostree.

I don't actually know if that rename suggestion is something we should do, just putting my thought process out there. Maybe just a comment would suffice.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it should probably be called was_oci_imported.

Comment thread src/cosalib/cmdlib.py
if os.environ.get('COSA_PRIVILEGED', '') == '1':
if is_oci_imported:
import_oci_archive(tmpdir, tarfile, buildmeta['buildid'])
elif os.environ.get('COSA_PRIVILEGED', '') == '1':
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not new to this PR but I don't really know why we need both an elif/else here for the legacy path based on COSA_PRIVILEGED. Would just using the unprivileged path work in both cases (just be slower)?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it would. The privileged path takes advantage of the fact that we already have a bare-user repo laying around that probably has a majority of the objects already.

Comment thread src/cosalib/cmdlib.py
extract_image_json(workdir, commit)


def import_oci_archive(parent_tmpd, ociarchive, ref):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing this to the legacy unpriv path above it seems like the biggest differences here are:

  1. handling of the blob refs
  2. we call ostree container image pull versus ostree container import is that right?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, yes. The framing is quite different. With ostree container import, the source of truth is an OSTree commit that was exported to a tarball and we're reconverting it into the exact original OSTree commit. With ostree container image pull, the source of truth is the container image, and we're importing that into the tmp repo to mesh with existing tooling.

Comment thread src/cosalib/cmdlib.py
encoding='utf-8').splitlines()
subprocess.check_call(['ostree', 'pull-local', '--repo', 'tmp/repo', tmpd] + blob_refs)

return subprocess.check_output(['ostree', 'rev-parse', '--repo', 'tmp/repo', ref], encoding='utf-8').strip()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might make things a little more obvious

Suggested change
return subprocess.check_output(['ostree', 'rev-parse', '--repo', 'tmp/repo', ref], encoding='utf-8').strip()
ostree_commit = subprocess.check_output(['ostree', 'rev-parse', '--repo', 'tmp/repo', ref], encoding='utf-8').strip()
return ostree_commit

Comment thread src/cosalib/cmdlib.py
@@ -354,6 +360,42 @@ def import_ostree_commit(workdir, buildpath, buildmeta, extract_json=True, parti
extract_image_json(workdir, commit)


Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment here mentioning some callers expect the extracting ostree commit hash string to be returned might be useful.

Copy link
Copy Markdown
Member

@jlebon jlebon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the reviews! Let's get this in and do tweaks in follow-ups.

Comment thread src/cosalib/cmdlib.py
Comment on lines +337 to +338
if is_oci_imported:
import_oci_archive(tmpdir, tarfile, buildmeta['buildid'])
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it should probably be called was_oci_imported.

Comment thread src/cosalib/cmdlib.py
if os.environ.get('COSA_PRIVILEGED', '') == '1':
if is_oci_imported:
import_oci_archive(tmpdir, tarfile, buildmeta['buildid'])
elif os.environ.get('COSA_PRIVILEGED', '') == '1':
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it would. The privileged path takes advantage of the fact that we already have a bare-user repo laying around that probably has a majority of the objects already.

Comment thread src/cosalib/cmdlib.py
extract_image_json(workdir, commit)


def import_oci_archive(parent_tmpd, ociarchive, ref):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, yes. The framing is quite different. With ostree container import, the source of truth is an OSTree commit that was exported to a tarball and we're reconverting it into the exact original OSTree commit. With ostree container image pull, the source of truth is the container image, and we're importing that into the tmp repo to mesh with existing tooling.

@jlebon jlebon merged commit 7a6e3a0 into coreos:main Jul 18, 2025
5 checks passed
jlebon added a commit to jlebon/coreos-assembler that referenced this pull request Jul 18, 2025
@jlebon
Copy link
Copy Markdown
Member

jlebon commented Jul 18, 2025

Follow-ups in #4227.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants