@@ -2,10 +2,12 @@ package distro
22
33import (
44 "fmt"
5+ "path"
56 "path/filepath"
67 "strings"
78
89 "github.com/moby/buildkit/client/llb"
10+ ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
911 "github.com/project-dalec/dalec"
1012 "github.com/project-dalec/dalec/packaging/linux/rpm"
1113)
@@ -40,10 +42,21 @@ type dnfInstallConfig struct {
4042
4143 // When true, don't omit docs from the installed RPMs.
4244 includeDocs bool
45+
46+ forceArch string
4347}
4448
4549type DnfInstallOpt func (* dnfInstallConfig )
4650
51+ // joinUnderRoot joins a rootfs path with an absolute container path.
52+ // We must not use filepath.Join(root, "/abs") because that drops `root`.
53+ func joinUnderRoot (root , abs string ) string {
54+ if root == "" {
55+ return abs
56+ }
57+ return path .Join (root , strings .TrimPrefix (abs , "/" ))
58+ }
59+
4760// see comment in tdnfInstall for why this additional option is needed
4861func DnfImportKeys (keys []string ) DnfInstallOpt {
4962 return func (cfg * dnfInstallConfig ) {
@@ -63,6 +76,12 @@ func DnfAtRoot(root string) DnfInstallOpt {
6376 }
6477}
6578
79+ func DnfForceArch (arch string ) DnfInstallOpt {
80+ return func (cfg * dnfInstallConfig ) {
81+ cfg .forceArch = arch
82+ }
83+ }
84+
6685func DnfDownloadAllDeps (dest string ) DnfInstallOpt {
6786 return func (cfg * dnfInstallConfig ) {
6887 cfg .downloadOnly = true
@@ -145,23 +164,33 @@ func dnfCommand(cfg *dnfInstallConfig, releaseVer string, exe string, dnfSubCmd
145164
146165 cacheDir := "/var/cache/" + exe
147166 if cfg .root != "" {
148- cacheDir = filepath . Join (cfg .root , cacheDir )
167+ cacheDir = joinUnderRoot (cfg .root , cacheDir )
149168 }
150169 installFlags := dnfInstallFlags (cfg )
151170 installFlags += " -y --setopt varsdir=/etc/dnf/vars --releasever=" + releaseVer + " "
171+ forceArch := cfg .forceArch
152172 installScriptDt := `#!/usr/bin/env bash
153173set -eux -o pipefail
154174
155175import_keys_path="` + importKeysPath + `"
156176cmd="` + exe + `"
157177install_flags="` + installFlags + `"
178+ force_arch="` + forceArch + `"
158179dnf_sub_cmd="` + strings .Join (dnfSubCmd , " " ) + `"
159180cache_dir="` + cacheDir + `"
160181
161182if [ -x "$import_keys_path" ]; then
162183 "$import_keys_path"
163184fi
164185
186+ if [ -n "$force_arch" ]; then
187+ if [ "$cmd" = "tdnf" ]; then
188+ echo "tdnf does not support --forcearch; cross-arch installs must use dnf" >&2
189+ exit 70
190+ fi
191+ install_flags="$install_flags --forcearch=$force_arch"
192+ fi
193+
165194$cmd $dnf_sub_cmd $install_flags "${@}"
166195`
167196 var runOpts []llb.RunOption
@@ -196,6 +225,53 @@ $cmd $dnf_sub_cmd $install_flags "${@}"
196225 return dalec .WithRunOptions (runOpts ... )
197226}
198227
228+ func (cfg * Config ) InstallIntoRoot (rootfsPath string , pkgs []string , targetArch string , buildPlat ocispecs.Platform ) llb.RunOption {
229+ return dalec .RunOptFunc (func (ei * llb.ExecInfo ) {
230+ // Ensure the package manager runs on the build/executor platform (native),
231+ // while installing into the mounted target rootfs via --installroot.
232+ bp := buildPlat
233+ ei .Constraints .Platform = & bp
234+
235+ installOpts := []DnfInstallOpt {
236+ DnfAtRoot (rootfsPath ),
237+ DnfForceArch (targetArch ),
238+ DnfInstallWithConstraints ([]llb.ConstraintsOpt {dalec .WithConstraint (& ei .Constraints )}),
239+ }
240+
241+ var installCfg dnfInstallConfig
242+ dnfInstallOptions (& installCfg , installOpts )
243+
244+ cacheKey := cfg .CacheName
245+ if cfg .CacheAddPlatform {
246+ cacheKey += "-" + targetArch
247+ }
248+ // Cross-arch installs always use dnf --forcearch --installroot
249+ runOpts := []llb.RunOption {
250+ DnfInstall (& installCfg , cfg .ReleaseVer , pkgs ),
251+ }
252+
253+ // Mount package manager caches under the target rootfs (may include multiple dirs).
254+ for _ , d := range cfg .CacheDir {
255+ if d == "" {
256+ continue
257+ }
258+ k := cacheKey
259+ if len (cfg .CacheDir ) > 1 {
260+ k = cacheKey + "-" + filepath .Base (d )
261+ }
262+ runOpts = append (runOpts ,
263+ llb .AddMount (
264+ joinUnderRoot (rootfsPath , d ),
265+ llb .Scratch (),
266+ llb .AsPersistentCacheDir (k , llb .CacheMountLocked ),
267+ ),
268+ )
269+ }
270+
271+ dalec .WithRunOptions (runOpts ... ).SetRunOption (ei )
272+ })
273+ }
274+
199275func DnfInstall (cfg * dnfInstallConfig , releaseVer string , pkgs []string ) llb.RunOption {
200276 return dnfCommand (cfg , releaseVer , "dnf" , append ([]string {"install" }, pkgs ... ), nil )
201277}
0 commit comments