Skip to content

Automotive initial work #928

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 22, 2025
Merged
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
44 changes: 37 additions & 7 deletions bib/cmd/bootc-image-builder/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ const DEFAULT_SIZE = uint64(10 * GibiByte)

type ManifestConfig struct {
// OCI image path (without the transport, that is always docker://)
Imgref string
Imgref string
BuildImgref string

ImageTypes imagetypes.ImageTypes

Expand All @@ -57,7 +58,8 @@ type ManifestConfig struct {
DistroDefPaths []string

// Extracted information about the source container image
SourceInfo *source.Info
SourceInfo *source.Info
BuildSourceInfo *source.Info

// RootFSType specifies the filesystem type for the root partition
RootFSType string
Expand Down Expand Up @@ -221,15 +223,35 @@ func genPartitionTable(c *ManifestConfig, customizations *blueprint.Customizatio
}
}

var partitionTable *disk.PartitionTable
switch {
// XXX: move into images library
case fsCust != nil && diskCust != nil:
return nil, fmt.Errorf("cannot combine disk and filesystem customizations")
case diskCust != nil:
return genPartitionTableDiskCust(c, diskCust, rng)
partitionTable, err = genPartitionTableDiskCust(c, diskCust, rng)
if err != nil {
return nil, err
}
default:
return genPartitionTableFsCust(c, fsCust, rng)
partitionTable, err = genPartitionTableFsCust(c, fsCust, rng)
if err != nil {
return nil, err
}
}

// Ensure ext4 rootfs has fs-verity enabled
rootfs := partitionTable.FindMountable("/")
if rootfs != nil {
switch elem := rootfs.(type) {
case *disk.Filesystem:
if elem.Type == "ext4" {
elem.MkfsOptions = append(elem.MkfsOptions, []disk.MkfsOption{disk.MkfsVerity}...)
}
}
}

return partitionTable, nil
}

// calcRequiredDirectorySizes will calculate the minimum sizes for /
Expand Down Expand Up @@ -335,17 +357,25 @@ func manifestForDiskImage(c *ManifestConfig, rng *rand.Rand) (*manifest.Manifest
Name: c.Imgref,
Local: true,
}
buildContainerSource := container.SourceSpec{
Source: c.BuildImgref,
Name: c.BuildImgref,
Local: true,
}

var customizations *blueprint.Customizations
if c.Config != nil {
customizations = c.Config.Customizations
}

img := image.NewBootcDiskImage(containerSource)
img := image.NewBootcDiskImage(containerSource, buildContainerSource)
img.Users = users.UsersFromBP(customizations.GetUsers())
img.Groups = users.GroupsFromBP(customizations.GetGroups())
// TODO: get from the bootc container instead of hardcoding it
img.SELinux = "targeted"
img.SELinux = c.SourceInfo.SELinuxPolicy
img.BuildSELinux = img.SELinux
if c.BuildSourceInfo != nil {
img.BuildSELinux = c.BuildSourceInfo.SELinuxPolicy
}

img.KernelOptionsAppend = []string{
"rw",
Expand Down
12 changes: 8 additions & 4 deletions bib/cmd/bootc-image-builder/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,9 @@ func findMountableSizeableFor(pt *disk.PartitionTable, needle string) (disk.Moun
func TestGenPartitionTableSetsRootfsForAllFilesystemsXFS(t *testing.T) {
rng := bib.CreateRand()

a, _ := arch.FromString("amd64")
cnf := &bib.ManifestConfig{
Architecture: arch.FromString("amd64"),
Architecture: a,
RootFSType: "xfs",
}
cus := &blueprint.Customizations{
Expand Down Expand Up @@ -406,8 +407,9 @@ func TestGenPartitionTableSetsRootfsForAllFilesystemsXFS(t *testing.T) {
func TestGenPartitionTableSetsRootfsForAllFilesystemsBtrfs(t *testing.T) {
rng := bib.CreateRand()

a, _ := arch.FromString("amd64")
cnf := &bib.ManifestConfig{
Architecture: arch.FromString("amd64"),
Architecture: a,
RootFSType: "btrfs",
}
cus := &blueprint.Customizations{}
Expand All @@ -429,8 +431,9 @@ func TestGenPartitionTableSetsRootfsForAllFilesystemsBtrfs(t *testing.T) {
func TestGenPartitionTableDiskCustomizationRunsValidateLayoutConstraints(t *testing.T) {
rng := bib.CreateRand()

a, _ := arch.FromString("amd64")
cnf := &bib.ManifestConfig{
Architecture: arch.FromString("amd64"),
Architecture: a,
RootFSType: "xfs",
}
cus := &blueprint.Customizations{
Expand Down Expand Up @@ -650,8 +653,9 @@ func TestGenPartitionTableDiskCustomizationSizes(t *testing.T) {
},
} {
t.Run(tc.name, func(t *testing.T) {
a, _ := arch.FromString("amd64")
cnf := &bib.ManifestConfig{
Architecture: arch.FromString("amd64"),
Architecture: a,
RootFSType: "xfs",
RootfsMinsize: tc.rootfsMinSize,
}
Expand Down
79 changes: 58 additions & 21 deletions bib/cmd/bootc-image-builder/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ func manifestFromCobra(cmd *cobra.Command, args []string, pbar progress.Progress
rpmCacheRoot, _ := cmd.Flags().GetString("rpmmd")
targetArch, _ := cmd.Flags().GetString("target-arch")
rootFs, _ := cmd.Flags().GetString("rootfs")
buildImgref, _ := cmd.Flags().GetString("build-container")
useLibrepo, _ := cmd.Flags().GetBool("use-librepo")

// If --local was given, warn in the case of --local or --local=true (true is the default), error in the case of --local=false
Expand All @@ -216,17 +217,23 @@ func manifestFromCobra(cmd *cobra.Command, args []string, pbar progress.Progress
}
}

if targetArch != "" && arch.FromString(targetArch) != arch.Current() {
// TODO: detect if binfmt_misc for target arch is
// available, e.g. by mounting the binfmt_misc fs into
// the container and inspects the files or by
// including tiny statically linked target-arch
// binaries inside our bib container
fmt.Fprintf(os.Stderr, "WARNING: target-arch is experimental and needs an installed 'qemu-user' package\n")
if slices.Contains(imgTypes, "iso") {
return nil, nil, fmt.Errorf("cannot build iso for different target arches yet")
if targetArch != "" {
target, err := arch.FromString(targetArch)
if err != nil {
return nil, nil, err
}
if target != arch.Current() {
// TODO: detect if binfmt_misc for target arch is
// available, e.g. by mounting the binfmt_misc fs into
// the container and inspects the files or by
// including tiny statically linked target-arch
// binaries inside our bib container
fmt.Fprintf(os.Stderr, "WARNING: target-arch is experimental and needs an installed 'qemu-user' package\n")
if slices.Contains(imgTypes, "iso") {
return nil, nil, fmt.Errorf("cannot build iso for different target arches yet")
}
cntArch = target
}
cntArch = arch.FromString(targetArch)
}
// TODO: add "target-variant", see https://github.com/osbuild/bootc-image-builder/pull/139/files#r1467591868

Expand Down Expand Up @@ -285,26 +292,55 @@ func manifestFromCobra(cmd *cobra.Command, args []string, pbar progress.Progress
return nil, nil, err
}

buildContainer := container
buildSourceinfo := sourceinfo
startedBuildContainer := false
defer func() {
if startedBuildContainer {
if err := buildContainer.Stop(); err != nil {
logrus.Warnf("error stopping container: %v", err)
}
}
}()

if buildImgref != "" {
buildContainer, err = podman_container.New(buildImgref)
if err != nil {
return nil, nil, err
}
startedBuildContainer = true

// Gather some data from the containers distro
buildSourceinfo, err = source.LoadInfo(buildContainer.Root())
if err != nil {
return nil, nil, err
}
} else {
buildImgref = imgref
}

// This is needed just for RHEL and RHSM in most cases, but let's run it every time in case
// the image has some non-standard dnf plugins.
if err := container.InitDNF(); err != nil {
if err := buildContainer.InitDNF(); err != nil {
return nil, nil, err
}
solver, err := container.NewContainerSolver(rpmCacheRoot, cntArch, sourceinfo)
solver, err := buildContainer.NewContainerSolver(rpmCacheRoot, cntArch, sourceinfo)
if err != nil {
return nil, nil, err
}

manifestConfig := &ManifestConfig{
Architecture: cntArch,
Config: config,
ImageTypes: imageTypes,
Imgref: imgref,
RootfsMinsize: cntSize * containerSizeToDiskSizeMultiplier,
DistroDefPaths: distroDefPaths,
SourceInfo: sourceinfo,
RootFSType: rootfsType,
UseLibrepo: useLibrepo,
Architecture: cntArch,
Config: config,
ImageTypes: imageTypes,
Imgref: imgref,
BuildImgref: buildImgref,
RootfsMinsize: cntSize * containerSizeToDiskSizeMultiplier,
DistroDefPaths: distroDefPaths,
SourceInfo: sourceinfo,
BuildSourceInfo: buildSourceinfo,
RootFSType: rootfsType,
UseLibrepo: useLibrepo,
}

manifest, repos, err := makeManifest(manifestConfig, solver, rpmCacheRoot)
Expand Down Expand Up @@ -644,6 +680,7 @@ func buildCobraCmdline() (*cobra.Command, error) {
}
manifestCmd.Flags().String("rpmmd", "/rpmmd", "rpm metadata cache directory")
manifestCmd.Flags().String("target-arch", "", "build for the given target architecture (experimental)")
manifestCmd.Flags().String("build-container", "", "Use a custom container for the image build")
manifestCmd.Flags().StringArray("type", []string{"qcow2"}, fmt.Sprintf("image types to build [%s]", imagetypes.Available()))
manifestCmd.Flags().Bool("local", true, "DEPRECATED: --local is now the default behavior, make sure to pull the container image before running bootc-image-builder")
if err := manifestCmd.Flags().MarkHidden("local"); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions bib/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ require (
github.com/BurntSushi/toml v1.5.1-0.20250403130103-3d3abc24416a
github.com/cheggaaa/pb/v3 v3.1.7
github.com/hashicorp/go-version v1.7.0
github.com/osbuild/blueprint v1.6.0
github.com/osbuild/blueprint v1.7.0
github.com/osbuild/image-builder-cli v0.0.0-20250331194259-63bb56e12db3
github.com/osbuild/images v0.145.0
github.com/osbuild/images v0.147.0
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.9.1
github.com/spf13/pflag v1.0.6
Expand Down
8 changes: 4 additions & 4 deletions bib/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -235,12 +235,12 @@ github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8=
github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
github.com/osbuild/blueprint v1.6.0 h1:HUV1w/dMxpgqOgVtHhfTZE3zRmWQkuW/qTfx9smKImI=
github.com/osbuild/blueprint v1.6.0/go.mod h1:0d3dlY8aSJ6jM6NHwBmJFF1VIySsp/GsDpcJQ0yrOqM=
github.com/osbuild/blueprint v1.7.0 h1:SpuoFtTc0pofX89EcMrxPCVSPcN4rFGAe/H/brEEBjs=
github.com/osbuild/blueprint v1.7.0/go.mod h1:LfxBgOupiH6h6dfFHAkHK9Kpj9Yd7cSHnQd6zIiuKlc=
github.com/osbuild/image-builder-cli v0.0.0-20250331194259-63bb56e12db3 h1:M3yYunKH4quwJLQrnFo7dEwCTKorafNC+AUqAo7m5Yo=
github.com/osbuild/image-builder-cli v0.0.0-20250331194259-63bb56e12db3/go.mod h1:0sEmiQiMo1ChSuOoeONN0RmsoZbQEvj2mlO2448gC5w=
github.com/osbuild/images v0.145.0 h1:ZbY13lP02dJ090TTKq8UrPjuDrijPWKUMZQEG0zVRpA=
github.com/osbuild/images v0.145.0/go.mod h1:jY21PhkxIozII4M0xCqZL7poLtFwDJlEGj88pb3lalQ=
github.com/osbuild/images v0.147.0 h1:vLl8xbbY4sUHHToFkC4MimWTrYWAgaHu5ea9JGRsQmU=
github.com/osbuild/images v0.147.0/go.mod h1:jY21PhkxIozII4M0xCqZL7poLtFwDJlEGj88pb3lalQ=
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f h1:/UDgs8FGMqwnHagNDPGOlts35QkhAZ8by3DR7nMih7M=
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
Expand Down
42 changes: 42 additions & 0 deletions bib/internal/source/source.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package source

import (
"bufio"
"errors"
"fmt"
"os"
"path"
Expand All @@ -27,6 +29,7 @@ type OSRelease struct {
type Info struct {
OSRelease OSRelease
UEFIVendor string
SELinuxPolicy string
ImageCustomization *blueprint.Customizations
}

Expand Down Expand Up @@ -63,6 +66,39 @@ func uefiVendor(root string) (string, error) {
return "", fmt.Errorf("cannot find UEFI vendor in %s", bootupdEfiDir)
}

func readSelinuxPolicy(root string) (string, error) {
configPath := "etc/selinux/config"
f, err := os.Open(path.Join(root, configPath))
if err != nil {
return "", fmt.Errorf("cannot read selinux config %s: %w", configPath, err)
}
// nolint:errcheck
defer f.Close()

policy := ""
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if len(line) == 0 {
continue
}
if strings.HasPrefix(line, "#") {
continue
}

parts := strings.SplitN(line, "=", 2)
if len(parts) != 2 {
return "", errors.New("selinux config: invalid input")
}
key := strings.TrimSpace(parts[0])
if key == "SELINUXTYPE" {
policy = strings.TrimSpace(parts[1])
}
}

return policy, nil
}

func readImageCustomization(root string) (*blueprint.Customizations, error) {
prefix := path.Join(root, bibPathPrefix)
config, err := buildconfig.LoadConfig(path.Join(prefix, "config.json"))
Expand Down Expand Up @@ -102,6 +138,11 @@ func LoadInfo(root string) (*Info, error) {
return nil, err
}

selinuxPolicy, err := readSelinuxPolicy(root)
if err != nil {
logrus.Debugf("cannot read selinux policy: %v, setting it to none", err)
}

var idLike []string
if osrelease["ID_LIKE"] != "" {
idLike = strings.Split(osrelease["ID_LIKE"], " ")
Expand All @@ -118,6 +159,7 @@ func LoadInfo(root string) (*Info, error) {
},

UEFIVendor: vendor,
SELinuxPolicy: selinuxPolicy,
ImageCustomization: customization,
}, nil
}
Loading