Skip to content

Commit

Permalink
manifest: use experimentalflags.String("bootstrap")
Browse files Browse the repository at this point in the history
This commit allows to use a container to bootstrap the buildroot
via the experimtnal "bootstrap" flag:
```
$ IMAGE_BUILDER_EXPERIMENTAL=bootstrap=ghcr.io/mvo5/fedora-buildroot:41 \
   image-builder manifest --arch=riscv64 minimal-raw --distro=fedora-41
```
and it will use the given container to bootstap the buildroot.

A bootstrap container has very few requirements:
- python3 for the runners
- rpm so that the real buildroot rpms can get instaleld
- setfiles so that the selinux stage for the real buildroot can run
(so most upstream containers, e.g. fedora-toolbox or ubi work).

This will allow to do cross-arch building for riscv. The generated
manifest can be build with osbuild (as long as qemu-user is installed)
and will generate a minimal-raw for riscv64.

Cross-building is useful because qemu-user is a lot faster
(and easier to setup) than full riscv system emulation.
A minimal raw build on a really fast AWS machine takes about
100 minutes. A full minimal-raw build on my (moderate) PC takes
about 20min - which is fast enough so that we can run this in
GH workflows as part of the CI.

The approach with the bootstrap buildroot [0] allows us to
generalize this later so that each distro would (in addition
to the repos) an upstream container ref. With that we could
auto-detect cross builds and we would just add this upstream
container to bootstrap the buildroot.

[0] Thanks to Ondrej, Achilleas, Thozza, Simon and Florian.
  • Loading branch information
mvo5 committed Feb 26, 2025
1 parent 9d95516 commit ec20905
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 0 deletions.
40 changes: 40 additions & 0 deletions pkg/manifest/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package manifest
import (
"fmt"

"github.com/osbuild/images/internal/common"
"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/experimentalflags"
"github.com/osbuild/images/pkg/osbuild"
"github.com/osbuild/images/pkg/rpmmd"
"github.com/osbuild/images/pkg/runner"
Expand Down Expand Up @@ -57,6 +59,11 @@ func NewBuild(m *Manifest, runner runner.Runner, repos []rpmmd.RepoConfig, opts
repos: filterRepos(repos, name),
containerBuildable: opts.ContainerBuildable,
}

// This allows to bootstrap the buildroot with a custom container
// for e.g. cross-arch-build experiments,
maybeAddExperimentalContainerBootstrap(m, runner, opts, pipeline)

m.addPipeline(pipeline)
return pipeline
}
Expand Down Expand Up @@ -149,6 +156,39 @@ func (p *BuildrootFromPackages) getSELinuxLabels() map[string]string {
return labels
}

// maybeAddExperimentalContainerBootstrap will return a container buildroot
// if the "IMAGE_BUILDER_EXPERIMENTAL=bootstrap=<container-ref>" is
// defined. This allows us to do cross-arch build experimentation.
//
// A "bootstrap" container has only these requirements:
// - python3 for the runners
// - rpm so that the real buildroot rpms can get instaleld
// - setfiles so that the selinux stage for the real buildroot can run
// (and does not even need a working dnf or repo setup).
func maybeAddExperimentalContainerBootstrap(m *Manifest, runner runner.Runner, opts *BuildOptions, build *BuildrootFromPackages) {
bootstrapBuildrootRef := experimentalflags.String("bootstrap")
if bootstrapBuildrootRef == "" {
return
}

cntSrcs := []container.SourceSpec{
{
Source: bootstrapBuildrootRef,
Name: bootstrapBuildrootRef,
TLSVerify: common.ToPtr(false),
},
}
name := "bootstrap-buildroot"
bootstrapPipeline := &BuildrootFromContainer{
Base: NewBase(name, nil),
runner: runner,
dependents: make([]Pipeline, 0),
containers: cntSrcs,
}
m.addPipeline(bootstrapPipeline)
build.build = bootstrapPipeline
}

type BuildrootFromContainer struct {
Base

Expand Down
36 changes: 36 additions & 0 deletions pkg/manifest/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/osbuild/images/internal/common"
"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/osbuild"
"github.com/osbuild/images/pkg/rpmmd"
Expand Down Expand Up @@ -146,3 +147,38 @@ func TestBuildFromContainerSpecsGetSelinuxLabelsWithContainerBuildable(t *testin
"/usr/bin/umount": "system_u:object_r:install_exec_t:s0",
})
}

func TestNewBuildWithExperimentalOverride(t *testing.T) {
for _, withForcedBuildroot := range []bool{false, true} {
if withForcedBuildroot {
t.Setenv("IMAGE_BUILDER_EXPERIMENTAL", "bootstrap=ghcr.io/ondrejbudai/cool:stuff")
} else {
t.Setenv("IMAGE_BUILDER_EXPERIMENTAL", "")
}
mf := New()
runner := &runner.Fedora{Version: 42}
buildIf := NewBuild(&mf, runner, nil, nil)
require.NotNil(t, buildIf)
if withForcedBuildroot {
bootstrapPipeline := mf.pipelines[0]
br, ok := bootstrapPipeline.(*BuildrootFromContainer)
assert.True(t, ok)
assert.Equal(t, []container.SourceSpec{
{
Source: "ghcr.io/ondrejbudai/cool:stuff",
Name: "ghcr.io/ondrejbudai/cool:stuff",
TLSVerify: common.ToPtr(false),
},
}, br.containers)

buildPipeline := mf.pipelines[1]
br2, ok := buildPipeline.(*BuildrootFromPackages)
assert.True(t, ok)
assert.Equal(t, bootstrapPipeline, br2.build)
} else {
br, ok := buildIf.(*BuildrootFromPackages)
assert.True(t, ok)
assert.Nil(t, br.build)
}
}
}

0 comments on commit ec20905

Please sign in to comment.