Skip to content

Commit

Permalink
bib: reoslve container content from the external dnf again
Browse files Browse the repository at this point in the history
This commit goes back to the apparoch we had in PR#565 to resolve using
the solver from outside of the container. The reason is that:
a) we don't need to run code from the container for this
b) some containers (like f41) do not have the python dnf available

Huge kudos to Achilleas for most of the work here.

Co-Authored-By: Achilleas Koutsou <[email protected]>
  • Loading branch information
mvo5 and achilleas-k committed Nov 5, 2024
1 parent 19acee6 commit 54fb24a
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 20 deletions.
74 changes: 54 additions & 20 deletions bib/internal/container/solver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package container

import (
"fmt"
"os"
"os/exec"
"path/filepath"

Expand Down Expand Up @@ -31,38 +32,71 @@ func (c *Container) InitDNF() error {
return nil
}

func (cnt *Container) injectDNFJson() ([]string, error) {
if err := cnt.CopyInto("/usr/libexec/osbuild-depsolve-dnf", "/osbuild-depsolve-dnf"); err != nil {
return nil, fmt.Errorf("cannot prepare depsolve in the container: %w", err)
func (cnt *Container) hasRunSecrets() bool {
_, err := os.Stat(filepath.Join(cnt.root, "/run/secrets/redhat.repo"))
return err == nil
}

// setupRunSecretsBindMount will synthesise a /run/secrets dir
// in the container root
func (cnt *Container) setupRunSecrets() error {
if cnt.hasRunSecrets() {
return nil
}
dst := filepath.Join(cnt.root, "/run/secrets")
if err := os.MkdirAll(dst, 0755); err != nil {
return err
}
// copy the python module too
globPath := "/usr/lib/*/site-packages/osbuild"
matches, err := filepath.Glob(globPath)
if err != nil || len(matches) == 0 {
return nil, fmt.Errorf("cannot find osbuild python module in %q: %w", globPath, err)

// We cannot just bind mount here because
// /usr/share/rhel/secrets contains a bunch of symlinks and
// we also want to be able to run "bootc-image-builder manifest"
// outside a container so supporting this is nice.
//
// So instead of bind mounting we create a copy of the
// /run/secrets/ - they are static so that should be fineq
for _, src := range []string{"/run/secrets", "/usr/share/rhel/secrets"} {
if st, err := os.Stat(src); err != nil || !st.IsDir() {
continue
}

dents, err := filepath.Glob(src + "/*")
if err != nil {
return err
}
for _, ent := range dents {
if output, err := exec.Command("cp", "-rvL", ent, dst).CombinedOutput(); err != nil {
return fmt.Errorf("failed to setup /run/secrets: %w, output:\n%s", err, string(output))
}
}
}
if len(matches) != 1 {
return nil, fmt.Errorf("unexpected number of osbuild python module matches: %v", matches)

// XXX: we need to twist arms here, f41 uses an aboslute link to link back to entitlements-host and rhsm-host , it needs to be relative for us
//
// XXX2: check golang for "ln -f" for atomic exchange
if output, err := exec.Command("ln", "-sf", "../../run/secrets/etc-pki-entitlement", filepath.Join(cnt.root, "/etc/pki/entitlement-host")).CombinedOutput(); err != nil {
return fmt.Errorf("cannot run ls: %werr, output:\n%s", err, output)
}
if err := cnt.CopyInto(matches[0], "/"); err != nil {
return nil, fmt.Errorf("cannot prepare depsolve python-modules in the container: %w", err)
if output, err := exec.Command("ln", "-sf", "../run/secrets/rhsm-host", filepath.Join(cnt.root, "/etc/rhsm-host")).CombinedOutput(); err != nil {
return fmt.Errorf("cannot run ls: %werr, output:\n%s", err, output)
}
return append(cnt.ExecArgv(), "/osbuild-depsolve-dnf"), nil

return nil
}

func (cnt *Container) NewContainerSolver(cacheRoot string, architecture arch.Arch, sourceInfo *source.Info) (*dnfjson.Solver, error) {
depsolverCmd, err := cnt.injectDNFJson()
if err != nil {
return nil, fmt.Errorf("cannot inject depsolve into the container: %w", err)
}

solver := dnfjson.NewSolver(
sourceInfo.OSRelease.PlatformID,
sourceInfo.OSRelease.VersionID,
architecture.String(),
fmt.Sprintf("%s-%s", sourceInfo.OSRelease.ID, sourceInfo.OSRelease.VersionID),
cacheRoot)
solver.SetDNFJSONPath(depsolverCmd[0], depsolverCmd[1:]...)
solver.SetRootDir("/")

// we copy the data directly into the cnt.root, no need to
// cleanup here because podman stop will remove the dir
if err := cnt.setupRunSecrets(); err != nil {
return nil, err
}
solver.SetRootDir(cnt.root)
return solver, nil
}
5 changes: 5 additions & 0 deletions bib/internal/container/solver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ func TestDNFJsonWorks(t *testing.T) {

cnt, err := container.New(dnfTestingImageCentos)
require.NoError(t, err)
defer cnt.Stop()

Check failure on line 36 in bib/internal/container/solver_test.go

View workflow job for this annotation

GitHub Actions / ⌨ Lint & unittests

Error return value of `cnt.Stop` is not checked (errcheck)

err = cnt.InitDNF()
require.NoError(t, err)

Expand Down Expand Up @@ -116,13 +118,16 @@ func TestDNFJsonWorkWithSubscribedContent(t *testing.T) {

cnt, err := container.New(dnfTestingImageRHEL)
require.NoError(t, err)
defer cnt.Stop()

Check failure on line 121 in bib/internal/container/solver_test.go

View workflow job for this annotation

GitHub Actions / ⌨ Lint & unittests

Error return value of `cnt.Stop` is not checked (errcheck)

err = cnt.InitDNF()
require.NoError(t, err)

sourceInfo, err := source.LoadInfo(cnt.Root())
require.NoError(t, err)
solver, err := cnt.NewContainerSolver(cacheRoot, arch.ARCH_X86_64, sourceInfo)
require.NoError(t, err)

res, err := solver.Depsolve([]rpmmd.PackageSet{
{
Include: []string{"coreutils"},
Expand Down

0 comments on commit 54fb24a

Please sign in to comment.