Skip to content

Add --build-container to run build pipeline and dnf in a custom container #921

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

Closed
wants to merge 2 commits into from
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
21 changes: 15 additions & 6 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 @@ -323,17 +325,22 @@ 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 = c.BuildSourceInfo.SELinuxPolicy

img.KernelOptionsAppend = []string{
"rw",
Expand Down Expand Up @@ -411,7 +418,9 @@ func manifestForDiskImage(c *ManifestConfig, rng *rand.Rand) (*manifest.Manifest
mf.Distro = manifest.DISTRO_FEDORA
runner := &runner.Linux{}

if err := img.InstantiateManifestFromContainers(&mf, []container.SourceSpec{containerSource}, runner, rng); err != nil {
if err := img.InstantiateManifestFromContainers(&mf,
[]container.SourceSpec{containerSource},
[]container.SourceSpec{buildContainerSource}, runner, rng); err != nil {
return nil, err
}

Expand Down
53 changes: 42 additions & 11 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 Down Expand Up @@ -297,26 +298,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 @@ -656,6 +686,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
46 changes: 43 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,36 @@ 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)
}
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 +104,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 +125,7 @@ func LoadInfo(root string) (*Info, error) {
IDLike: idLike,
},

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