Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/pytest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ jobs:
- name: Install cross build dependencies
run: sudo apt install -y qemu-user-static

- name: Run integration tests via pytest
- name: Run integration tests via pytest (root)
run: |
# use "-s" for now for easier debugging
sudo pytest -s -v

- name: Run integration tests via pytest (non-root)
run: |
# use "-s" for now for easier debugging
pytest -s -v
5 changes: 4 additions & 1 deletion Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ FROM registry.fedoraproject.org/fedora:41
# podman mount needs this
RUN mkdir -p /etc/containers/networks
# Fast-track osbuild so we don't depend on the "slow" Fedora release process to implement new features in bib
# Add kernel, iproute, dhcp-client, etc for rootless builds via supermin
RUN dnf install -y dnf-plugins-core \
&& dnf copr enable -y @osbuild/osbuild \
&& dnf install -y libxcrypt-compat wget osbuild osbuild-ostree osbuild-depsolve-dnf osbuild-lvm2 \
&& dnf install -y libxcrypt-compat wget osbuild osbuild-ostree \
osbuild-depsolve-dnf osbuild-lvm2 supermin qemu-kvm qemu-user-static \
podman iproute dhcp-client kernel lvm2 rsync virtiofsd \
&& dnf clean all

COPY --from=builder /build/image-builder /usr/bin/
Expand Down
21 changes: 15 additions & 6 deletions cmd/image-builder/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ import (
)

var (
GetOneImage = getOneImage
Run = run
FindDistro = findDistro
DescribeImage = describeImage
ProgressFromCmd = progressFromCmd
BasenameFor = basenameFor
GetOneImage = getOneImage
Run = run
FindDistro = findDistro
DescribeImage = describeImage
ProgressFromCmd = progressFromCmd
BasenameFor = basenameFor
ImageBuilderDefaultCacheDir = imageBuilderDefaultCacheDir
)

func MockOsArgs(new []string) (restore func()) {
Expand All @@ -44,6 +45,14 @@ func MockOsStderr(new io.Writer) (restore func()) {
}
}

func MockOsGetuid(new func() int) (restore func()) {
saved := osGetuid
osGetuid = new
return func() {
osGetuid = saved
}
}

func MockNewRepoRegistry(f func() (*reporegistry.RepoRegistry, error)) (restore func()) {
saved := newRepoRegistry
newRepoRegistry = func(dataDir string, extraRepos []string) (*reporegistry.RepoRegistry, error) {
Expand Down
24 changes: 22 additions & 2 deletions cmd/image-builder/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,6 @@ func cmdBuild(cmd *cobra.Command, args []string) error {
if errors.Is(err, ErrUploadTypeUnsupported) || errors.Is(err, ErrUploadConfigNotProvided) {
err = nil
}

if err != nil {
return err
}
Expand Down Expand Up @@ -408,6 +407,23 @@ func cmdDescribeImg(cmd *cobra.Command, args []string) error {
return describeImage(res, osStdout)
}

var osGetuid = os.Getuid

func imageBuilderDefaultCacheDir() (string, error) {
if osGetuid() == 0 {
return "/var/cache/image-builder/store", nil
}
xdgCacheHome := os.Getenv("XDG_CACHE_HOME")
if xdgCacheHome == "" {
homeDir, err := os.UserHomeDir()
if err != nil {
return "", err
}
xdgCacheHome = filepath.Join(homeDir, ".cache")
}
return filepath.Join(xdgCacheHome, "image-builder/store"), nil
}

Comment on lines +410 to +426
Copy link
Member

Choose a reason for hiding this comment

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

Can we split this into a separate PR? I like it but I think we'll have some more discussion in this PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sure - I'm curious about your thoughts here, what extra discussion do you have in min? This change in itself is only useful if in combination with supermin, otherwise there will never be a user cache with actual files (as only manifest can run as non-root without supermin)

Copy link
Member

Choose a reason for hiding this comment

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

Only disk image types require privileges, there are a few that don't.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That is interesting. We do require bind mount in osbuild (and maybe more for bwrap) so a user container with --privileged might be enough for non-disk types (like contianer). I have not tested this though.

func run() error {
// images generates a lot of noisy logs a bunch of stuff to
// Debug/Info that is distracting the user (at least by
Expand Down Expand Up @@ -483,6 +499,10 @@ operating systems like Fedora, CentOS and RHEL with easy customizations support.
uploadCmd.Flags().String("aws-region", "", "target region for AWS uploads (only for type=ami)")
rootCmd.AddCommand(uploadCmd)

ibCacheDir, err := imageBuilderDefaultCacheDir()
if err != nil {
return err
}
buildCmd := &cobra.Command{
Use: "build <image-type>",
Short: "Build the given image-type, e.g. qcow2 (tip: combine with --distro, --arch)",
Expand All @@ -494,7 +514,7 @@ operating systems like Fedora, CentOS and RHEL with easy customizations support.
buildCmd.Flags().Bool("with-manifest", false, `export osbuild manifest`)
buildCmd.Flags().Bool("with-buildlog", false, `export osbuild buildlog`)
// XXX: add --rpmmd cache too and put under /var/cache/image-builder/dnf
buildCmd.Flags().String("cache", "/var/cache/image-builder/store", `osbuild directory to cache intermediate build artifacts"`)
buildCmd.Flags().String("cache", ibCacheDir, `osbuild directory to cache intermediate build artifacts"`)
// XXX: add "--verbose" here, similar to how bib is doing this
// (see https://github.com/osbuild/bootc-image-builder/pull/790/commits/5cec7ffd8a526e2ca1e8ada0ea18f927695dfe43)
buildCmd.Flags().String("progress", "auto", "type of progress bar to use (e.g. verbose,term)")
Expand Down
34 changes: 34 additions & 0 deletions cmd/image-builder/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ func TestManifestIntegrationSmoke(t *testing.T) {
if !hasDepsolveDnf() {
t.Skip("no osbuild-depsolve-dnf binary found")
}
t.Setenv("IMAGE_BUILDER_EXPERIMENTAL", "skip-priv-checks")

restore := main.MockNewRepoRegistry(testrepos.New)
defer restore()
Expand Down Expand Up @@ -364,6 +365,7 @@ func TestBuildIntegrationHappy(t *testing.T) {
if !hasDepsolveDnf() {
t.Skip("no osbuild-depsolve-dnf binary found")
}
t.Setenv("IMAGE_BUILDER_EXPERIMENTAL", "skip-priv-checks")

restore := main.MockNewRepoRegistry(testrepos.New)
defer restore()
Expand Down Expand Up @@ -425,6 +427,7 @@ func TestBuildIntegrationArgs(t *testing.T) {
if !hasDepsolveDnf() {
t.Skip("no osbuild-depsolve-dnf binary found")
}
t.Setenv("IMAGE_BUILDER_EXPERIMENTAL", "skip-priv-checks")

restore := main.MockNewRepoRegistry(testrepos.New)
defer restore()
Expand Down Expand Up @@ -514,6 +517,7 @@ func TestBuildIntegrationErrorsProgressVerbose(t *testing.T) {
if !hasDepsolveDnf() {
t.Skip("no osbuild-depsolve-dnf binary found")
}
t.Setenv("IMAGE_BUILDER_EXPERIMENTAL", "skip-priv-checks")

restore := main.MockNewRepoRegistry(testrepos.New)
defer restore()
Expand Down Expand Up @@ -548,6 +552,7 @@ func TestBuildIntegrationErrorsProgressVerboseWithBuildlog(t *testing.T) {
if !hasDepsolveDnf() {
t.Skip("no osbuild-depsolve-dnf binary found")
}
t.Setenv("IMAGE_BUILDER_EXPERIMENTAL", "skip-priv-checks")

restore := main.MockNewRepoRegistry(testrepos.New)
defer restore()
Expand Down Expand Up @@ -597,6 +602,7 @@ func TestBuildIntegrationErrorsProgressTerm(t *testing.T) {
if !hasDepsolveDnf() {
t.Skip("no osbuild-depsolve-dnf binary found")
}
t.Setenv("IMAGE_BUILDER_EXPERIMENTAL", "skip-priv-checks")

restore := main.MockNewRepoRegistry(testrepos.New)
defer restore()
Expand Down Expand Up @@ -852,6 +858,7 @@ func TestBuildCrossArchSmoke(t *testing.T) {
if !hasDepsolveDnf() {
t.Skip("no osbuild-depsolve-dnf binary found")
}
t.Setenv("IMAGE_BUILDER_EXPERIMENTAL", "skip-priv-checks")

restore := main.MockNewRepoRegistry(testrepos.New)
defer restore()
Expand Down Expand Up @@ -904,6 +911,7 @@ func TestBuildIntegrationOutputFilename(t *testing.T) {
if !hasDepsolveDnf() {
t.Skip("no osbuild-depsolve-dnf binary found")
}
t.Setenv("IMAGE_BUILDER_EXPERIMENTAL", "skip-priv-checks")

restore := main.MockNewRepoRegistry(testrepos.New)
defer restore()
Expand Down Expand Up @@ -1058,3 +1066,29 @@ func TestManifestIntegrationWithRegistrations(t *testing.T) {
assert.Contains(t, fakeStdout.String(), `"type":"org.osbuild.systemd.unit.create","options":{"filename":"osbuild-subscription-register.service"`)
assert.Contains(t, fakeStdout.String(), `server_url_123`)
}

func TestImageBuilderDefaultCacheDir(t *testing.T) {
homeDir, err := os.UserHomeDir()
require.NoError(t, err)

for _, tc := range []struct {
uid int
xdgCacheHomeEnv string
expected string
}{
{0, "", "/var/cache/image-builder/store"},
{100, "", filepath.Join(homeDir, ".cache/image-builder/store")},
{100, "/home/user/my-cache", "/home/user/my-cache/image-builder/store"},
} {
restore := main.MockOsGetuid(func() int {
return tc.uid
})
defer restore()

t.Setenv("XDG_CACHE_HOME", tc.xdgCacheHomeEnv)

cacheDir, err := main.ImageBuilderDefaultCacheDir()
assert.NoError(t, err)
assert.Equal(t, tc.expected, cacheDir)
}
}
2 changes: 2 additions & 0 deletions cmd/image-builder/upload_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ func TestBuildAndUploadWithAWSMock(t *testing.T) {
if !hasDepsolveDnf() {
t.Skip("no osbuild-depsolve-dnf binary found")
}
t.Setenv("IMAGE_BUILDER_EXPERIMENTAL", "skip-priv-checks")

var regionName, bucketName, amiName string
var fa fakeAwsUploader
Expand Down Expand Up @@ -177,6 +178,7 @@ func TestBuildAmiButNotUpload(t *testing.T) {
if !hasDepsolveDnf() {
t.Skip("no osbuild-depsolve-dnf binary found")
}
t.Setenv("IMAGE_BUILDER_EXPERIMENTAL", "skip-priv-checks")

fa := fakeAwsUploader{
uploadAndRegisterErr: fmt.Errorf("upload should not be called"),
Expand Down
33 changes: 24 additions & 9 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,34 @@

set -e

# TODO: share code with bib to do the setup automatically
# see https://github.com/teamsbc/container-for-osbuild/blob/main/entrypoint.bash (thanks simon)
# and https://github.com/osbuild/bootc-image-builder/blob/main/bib/internal/setup/setup.go#L21 (thanks ondrej,achilleas,colin)
mkdir /run/osbuild
if grep -q "rootless=1" /run/.containerenv 2>/dev/null; then
PODMAN_ROOTLESS=1
else
PODMAN_ROOTLESS=0
fi
if capsh --has-p=cap_sys_admin 2>/dev/null; then
PODMAN_PRIVILEGED=1
else
PODMAN_PRIVILEGED=0
fi

mount -t tmpfs tmpfs /run/osbuild
# setup container so that it can be used to build images directly without
# supermin
if [ "$PODMAN_ROOTLESS" = "0" ] && [ "$PODMAN_PRIVILEGED" = "1" ]; then
# TODO: share code with bib to do the setup automatically
# see https://github.com/teamsbc/container-for-osbuild/blob/main/entrypoint.bash (thanks simon)
# and https://github.com/osbuild/bootc-image-builder/blob/main/bib/internal/setup/setup.go#L21 (thanks ondrej,achilleas,colin)
mkdir /run/osbuild

cp -p /usr/bin/osbuild /run/osbuild/osbuild
mount -t tmpfs tmpfs /run/osbuild

chcon system_u:object_r:install_exec_t:s0 /run/osbuild/osbuild
cp -p /usr/bin/osbuild /run/osbuild/osbuild

mount -t devtmpfs devtmpfs /dev
mount --bind /run/osbuild/osbuild /usr/bin/osbuild
chcon system_u:object_r:install_exec_t:s0 /run/osbuild/osbuild

mount -t devtmpfs devtmpfs /dev
mount --bind /run/osbuild/osbuild /usr/bin/osbuild
fi

# XXX: make this nicer
cd /output
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/BurntSushi/toml v1.5.1-0.20250403130103-3d3abc24416a
github.com/cheggaaa/pb/v3 v3.1.7
github.com/gobwas/glob v0.2.3
github.com/google/uuid v1.6.0
github.com/mattn/go-isatty v0.0.20
github.com/osbuild/blueprint v1.5.0
github.com/osbuild/images v0.135.0
Expand Down Expand Up @@ -66,7 +67,6 @@ require (
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-containerregistry v0.20.2 // indirect
github.com/google/go-intervals v0.0.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
Expand Down
12 changes: 11 additions & 1 deletion pkg/progress/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ type (
)

var (
NewSyncedWriter = newSyncedWriter
NewSyncedWriter = newSyncedWriter
EnoughPrivsForOsbuild = enoughPrivsForOsbuild
WaitForFiles = waitForFiles
)

func MockOsStdout(w io.Writer) (restore func()) {
Expand Down Expand Up @@ -45,3 +47,11 @@ func MockOsbuildCmd(s string) (restore func()) {
osbuildCmd = saved
}
}

func MockEnoughPrivsForOsbuild(new func() (bool, error)) (restore func()) {
saved := enoughPrivsForOsbuild
enoughPrivsForOsbuild = new
return func() {
enoughPrivsForOsbuild = saved
}
}
Loading
Loading