Skip to content
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
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package main

import (
cryptorand "crypto/rand"
"fmt"
"math"
"math/big"
"math/rand"
"slices"
"strconv"
Expand All @@ -16,6 +13,7 @@ import (
"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/customizations/anaconda"
"github.com/osbuild/images/pkg/customizations/kickstart"
"github.com/osbuild/images/pkg/depsolvednf"
"github.com/osbuild/images/pkg/disk"
"github.com/osbuild/images/pkg/image"
"github.com/osbuild/images/pkg/manifest"
Expand All @@ -25,17 +23,25 @@ import (
"github.com/osbuild/images/pkg/runner"
"github.com/sirupsen/logrus"

podman_container "github.com/osbuild/images/pkg/bib/container"

"github.com/osbuild/bootc-image-builder/bib/internal/distrodef"
"github.com/osbuild/bootc-image-builder/bib/internal/imagetypes"
)

// all possible locations for the bib's distro definitions
// ./data/defs and ./bib/data/defs are for development
// /usr/share/bootc-image-builder/defs is for the production, containerized version
var distroDefPaths = []string{
"./data/defs",
"./bib/data/defs",
"/usr/share/bootc-image-builder/defs",
}

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

ImageTypes imagetypes.ImageTypes

// Build config
Config *blueprint.Blueprint

Expand All @@ -56,6 +62,155 @@ type ManifestConfig struct {
UseLibrepo bool
}

func manifestFromCobraForLegacyISO(imgref, buildImgref, imgTypeStr, rootFs, rpmCacheRoot string, config *blueprint.Blueprint, useLibrepo bool, cntArch arch.Arch) ([]byte, *mTLSConfig, error) {
container, err := podman_container.New(imgref)
if err != nil {
return nil, nil, err
}
defer func() {
if err := container.Stop(); err != nil {
logrus.Warnf("error stopping container: %v", err)
}
}()

var rootfsType string
if rootFs != "" {
rootfsType = rootFs
} else {
rootfsType, err = container.DefaultRootfsType()
if err != nil {
return nil, nil, fmt.Errorf("cannot get rootfs type for container: %w", err)
}
if rootfsType == "" {
return nil, nil, fmt.Errorf(`no default root filesystem type specified in container, please use "--rootfs" to set manually`)
}
}

// Gather some data from the containers distro
sourceinfo, err := osinfo.Load(container.Root())
if err != nil {
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 = osinfo.Load(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 := buildContainer.InitDNF(); err != nil {
return nil, nil, err
}
solver, err := buildContainer.NewContainerSolver(rpmCacheRoot, cntArch, sourceinfo)
if err != nil {
return nil, nil, err
}

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

manifest, repos, err := makeISOManifest(manifestConfig, solver, rpmCacheRoot)
if err != nil {
return nil, nil, err
}

mTLS, err := extractTLSKeys(repos)
if err != nil {
return nil, nil, err
}

return manifest, mTLS, nil
}

func makeISOManifest(c *ManifestConfig, solver *depsolvednf.Solver, cacheRoot string) (manifest.OSBuildManifest, map[string][]rpmmd.RepoConfig, error) {
rng := createRand()
mani, err := manifestForISO(c, rng)
if err != nil {
return nil, nil, fmt.Errorf("cannot get manifest: %w", err)
}

// depsolve packages
depsolvedSets := make(map[string]depsolvednf.DepsolveResult)
depsolvedRepos := make(map[string][]rpmmd.RepoConfig)
for name, pkgSet := range mani.GetPackageSetChains() {
res, err := solver.Depsolve(pkgSet, 0)
if err != nil {
return nil, nil, fmt.Errorf("cannot depsolve: %w", err)
}
depsolvedSets[name] = *res
depsolvedRepos[name] = res.Repos
}

// Resolve container - the normal case is that host and target
// architecture are the same. However it is possible to build
// cross-arch images by using qemu-user. This will run everything
// (including the build-root) with the target arch then, it
// is fast enough (given that it's mostly I/O and all I/O is
// run naively via syscall translation)

// XXX: should NewResolver() take "arch.Arch"?
resolver := container.NewResolver(c.Architecture.String())

containerSpecs := make(map[string][]container.Spec)
for plName, sourceSpecs := range mani.GetContainerSourceSpecs() {
for _, c := range sourceSpecs {
resolver.Add(c)
}
specs, err := resolver.Finish()
if err != nil {
return nil, nil, fmt.Errorf("cannot resolve containers: %w", err)
}
for _, spec := range specs {
if spec.Arch != c.Architecture {
return nil, nil, fmt.Errorf("image found is for unexpected architecture %q (expected %q), if that is intentional, please make sure --target-arch matches", spec.Arch, c.Architecture)
}
}
containerSpecs[plName] = specs
}

var opts manifest.SerializeOptions
if c.UseLibrepo {
opts.RpmDownloader = osbuild.RpmDownloaderLibrepo
}
mf, err := mani.Serialize(depsolvedSets, containerSpecs, nil, &opts)
if err != nil {
return nil, nil, fmt.Errorf("[ERROR] manifest serialization failed: %s", err.Error())
}
return mf, depsolvedRepos, nil
}

func labelForISO(os *osinfo.OSRelease, arch *arch.Arch) string {
switch os.ID {
case "fedora":
Expand Down Expand Up @@ -256,14 +411,3 @@ func getDistroAndRunner(osRelease osinfo.OSRelease) (manifest.Distro, runner.Run
logrus.Warnf("Unknown distro %s, using default runner", osRelease.ID)
return manifest.DISTRO_NULL, &runner.Linux{}, nil
}

func createRand() *rand.Rand {
seed, err := cryptorand.Int(cryptorand.Reader, big.NewInt(math.MaxInt64))
if err != nil {
panic("Cannot generate an RNG seed.")
}

// math/rand is good enough in this case
/* #nosec G404 */
return rand.New(rand.NewSource(seed.Int64()))
}
Loading
Loading