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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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 @@ -209,15 +211,35 @@ func genPartitionTable(c *ManifestConfig, customizations *blueprint.Customizatio
if err != nil {
return nil, fmt.Errorf("error reading disk customizations: %w", err)
}
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 @@ -323,17 +345,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
2 changes: 2 additions & 0 deletions bib/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,5 @@ require (
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)

replace github.com/osbuild/images => github.com/alexlarsson/osbuild-images v0.0.0-20250516113537-a29ea0d42b03
4 changes: 2 additions & 2 deletions bib/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1o
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
github.com/alexlarsson/osbuild-images v0.0.0-20250516113537-a29ea0d42b03 h1:+ADbFl/OFhoaOZJH93urWXR+ERryiDc5BmLAb3xKKKc=
github.com/alexlarsson/osbuild-images v0.0.0-20250516113537-a29ea0d42b03/go.mod h1:jY21PhkxIozII4M0xCqZL7poLtFwDJlEGj88pb3lalQ=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE=
Expand Down Expand Up @@ -239,8 +241,6 @@ github.com/osbuild/blueprint v1.6.0 h1:HUV1w/dMxpgqOgVtHhfTZE3zRmWQkuW/qTfx9smKI
github.com/osbuild/blueprint v1.6.0/go.mod h1:0d3dlY8aSJ6jM6NHwBmJFF1VIySsp/GsDpcJQ0yrOqM=
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/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
49 changes: 46 additions & 3 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 @@ -21,8 +23,9 @@ type OSRelease struct {
}

type Info struct {
OSRelease OSRelease
UEFIVendor string
OSRelease OSRelease
UEFIVendor string
SELinuxPolicy string
}

func validateOSRelease(osrelease map[string]string) error {
Expand Down Expand Up @@ -58,6 +61,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)
}
Comment on lines +66 to +69
Copy link
Member

Choose a reason for hiding this comment

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

The file isn't explicitly closed.

Copy link
Author

Choose a reason for hiding this comment

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

fixed

// 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 LoadInfo(root string) (*Info, error) {
osrelease, err := distro.ReadOSReleaseFromTree(root)
if err != nil {
Expand All @@ -71,6 +107,12 @@ func LoadInfo(root string) (*Info, error) {
if err != nil {
logrus.Debugf("cannot read UEFI vendor: %v, setting it to none", 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 @@ -86,6 +128,7 @@ func LoadInfo(root string) (*Info, error) {
IDLike: idLike,
},

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