Skip to content

Commit ebdee65

Browse files
committed
azlinux: Use native dnf to install packages
When cross compiling, use dnf from the native platform to install packages into the target arch rootfs. This helps speed up package install a bit since dnf won't need to run under emulation. Signed-off-by: Brian Goff <[email protected]>
1 parent 50da741 commit ebdee65

File tree

5 files changed

+115
-67
lines changed

5 files changed

+115
-67
lines changed

frontend/azlinux/azlinux3.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package azlinux
33
import (
44
"context"
55
"encoding/json"
6-
"path/filepath"
76

87
"github.com/Azure/dalec"
98
"github.com/moby/buildkit/client/llb"
@@ -49,6 +48,8 @@ func (w azlinux3) Base(sOpt dalec.SourceOpts, opts ...llb.ConstraintsOpt) (llb.S
4948

5049
img := llb.Image(Azlinux3Ref, llb.WithMetaResolver(sOpt.Resolver), dalec.WithConstraints(opts...))
5150
return img.Run(
51+
w.Install([]string{"dnf"}, installWithConstraints(opts), tdnfOnly),
52+
).Run(
5253
w.Install([]string{"rpm-build", "mariner-rpm-macros", "build-essential", "ca-certificates"}, installWithConstraints(opts)),
5354
dalec.WithConstraints(opts...),
5455
).Root(), nil
@@ -57,7 +58,7 @@ func (w azlinux3) Base(sOpt dalec.SourceOpts, opts ...llb.ConstraintsOpt) (llb.S
5758
func (w azlinux3) Install(pkgs []string, opts ...installOpt) llb.RunOption {
5859
var cfg installConfig
5960
setInstallOptions(&cfg, opts)
60-
return dalec.WithRunOptions(tdnfInstall(&cfg, "3.0", pkgs), w.tdnfCacheMount(cfg.root))
61+
return dalec.WithRunOptions(dnfInstall(&cfg, "3.0", pkgs, AzLinux3TargetKey))
6162
}
6263

6364
func (w azlinux3) BasePackages() []string {
@@ -91,7 +92,3 @@ func (azlinux3) WorkerImageConfig(ctx context.Context, resolver llb.ImageMetaRes
9192

9293
return &cfg, nil
9394
}
94-
95-
func (azlinux3) tdnfCacheMount(root string) llb.RunOption {
96-
return llb.AddMount(filepath.Join(root, tdnfCacheDir), llb.Scratch(), llb.AsPersistentCacheDir(tdnfCacheNameAzlinux3, llb.CacheMountLocked))
97-
}

frontend/azlinux/handle_rpm.go

Lines changed: 56 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,31 @@ func handleRPM(w worker) gwclient.BuildFunc {
5656
}
5757
}
5858

59-
type installFunc func(dalec.SourceOpts) (llb.RunOption, error)
59+
func platformFuzzyMatches(p *ocispecs.Platform) bool {
60+
if p == nil {
61+
return true
62+
}
63+
64+
// Note, this is intentionally not doing a strict match here
65+
// (e.g. [platforms.OnlyStrict])
66+
// This is used to see if we can get some optimizations when building for a
67+
// non-native platformm and in most cases the [platforms.Only] vector handles
68+
// things like building armv7 on an arm64 machine, which should be able to run
69+
// natively.
70+
return platforms.Only(platforms.DefaultSpec()).Match(*p)
71+
}
72+
73+
func installBuildDeps(w worker, sOpt dalec.SourceOpts, spec *dalec.Spec, targetKey string, platform *ocispecs.Platform, opts ...llb.ConstraintsOpt) (llb.StateOption, error) {
74+
deps := spec.GetBuildDeps(targetKey)
75+
if len(deps) == 0 {
76+
return func(in llb.State) llb.State { return in }, nil
77+
}
78+
79+
opts = append(opts, dalec.ProgressGroup("Install build deps"))
6080

61-
// Creates and installs an rpm meta-package that requires the passed in deps as runtime-dependencies
62-
func installBuildDepsPackage(target string, packageName string, w worker, sOpt dalec.SourceOpts, deps map[string]dalec.PackageConstraints, platform *ocispecs.Platform, installOpts ...installOpt) installFunc {
6381
// depsOnly is a simple dalec spec that only includes build dependencies and their constraints
6482
depsOnly := dalec.Spec{
65-
Name: fmt.Sprintf("%s-build-dependencies", packageName),
83+
Name: spec.Name + "-build-dependencies",
6684
Description: "Provides build dependencies for mariner2 and azlinux3",
6785
Version: "1.0",
6886
License: "Apache 2.0",
@@ -72,46 +90,42 @@ func installBuildDepsPackage(target string, packageName string, w worker, sOpt d
7290
},
7391
}
7492

75-
return func(Opt dalec.SourceOpts) (llb.RunOption, error) {
76-
pg := dalec.ProgressGroup("Building container for build dependencies")
93+
// create an RPM with just the build dependencies, using our same base worker
94+
rpmDir, err := createRPM(w, sOpt, &depsOnly, targetKey, platform, opts...)
95+
if err != nil {
96+
return nil, err
97+
}
98+
99+
rpmMountDir := "/tmp/rpms"
100+
pkg := []string{"/tmp/rpms/*/*.rpm"}
77101

78-
// create an RPM with just the build dependencies, using our same base worker
79-
rpmDir, err := createRPM(w, sOpt, &depsOnly, target, platform, pg)
102+
if !platformFuzzyMatches(platform) {
103+
base, err := w.Base(sOpt, opts...)
80104
if err != nil {
81105
return nil, err
82106
}
83107

84-
var opts []llb.ConstraintsOpt
85-
opts = append(opts, dalec.ProgressGroup("Install build deps"))
86-
87-
rpmMountDir := "/tmp/rpms"
88-
89-
installOpts = append([]installOpt{
90-
noGPGCheck,
91-
withMounts(llb.AddMount(rpmMountDir, rpmDir, llb.SourcePath("/RPMS"))),
92-
installWithConstraints(opts),
93-
}, installOpts...)
94-
95-
// install the built RPMs into the worker itself
96-
return w.Install([]string{"/tmp/rpms/*/*.rpm"}, installOpts...), nil
97-
}
98-
}
99-
100-
func installBuildDeps(w worker, sOpt dalec.SourceOpts, spec *dalec.Spec, targetKey string, platform *ocispecs.Platform, opts ...llb.ConstraintsOpt) (llb.StateOption, error) {
101-
deps := spec.GetBuildDeps(targetKey)
102-
if len(deps) == 0 {
103-
return func(in llb.State) llb.State { return in }, nil
104-
}
105-
106-
opts = append(opts, dalec.ProgressGroup("Install build deps"))
107-
108-
installOpt, err := installBuildDepsPackage(targetKey, spec.Name, w, sOpt, deps, platform, installWithConstraints(opts))(sOpt)
109-
if err != nil {
110-
return nil, err
108+
return func(in llb.State) llb.State {
109+
return base.Run(
110+
w.Install(
111+
pkg,
112+
withMounts(llb.AddMount(rpmMountDir, rpmDir, llb.SourcePath("/RPMS"))),
113+
atRoot("/tmp/rootfs"),
114+
withPlatform(platform),
115+
),
116+
).AddMount("/tmp/rootfs", in)
117+
}, nil
111118
}
112119

113120
return func(in llb.State) llb.State {
114-
return in.Run(installOpt, dalec.WithConstraints(opts...)).Root()
121+
return in.Run(
122+
w.Install(
123+
[]string{"/tmp/rpms/*/*.rpm"},
124+
withMounts(llb.AddMount(rpmMountDir, rpmDir, llb.SourcePath("/RPMS"))),
125+
installWithConstraints(opts),
126+
),
127+
dalec.WithConstraints(opts...),
128+
).Root()
115129
}, nil
116130
}
117131

@@ -233,15 +247,13 @@ func createRPM(w worker, sOpt dalec.SourceOpts, spec *dalec.Spec, targetKey stri
233247
}
234248

235249
var runOpts []llb.RunOption
236-
if platform != nil {
237-
if platforms.Only(platforms.DefaultSpec()).Match(*platform) && hasGolangBuildDep(spec, targetKey) {
238-
native, err := rpmWorker(w, sOpt, spec, targetKey, nil, opts...)
239-
if err != nil {
240-
return llb.Scratch(), err
241-
}
242-
243-
runOpts = append(runOpts, nativeGoMount(native, platform))
250+
if !platformFuzzyMatches(platform) && hasGolangBuildDep(spec, targetKey) {
251+
native, err := rpmWorker(w, sOpt, spec, targetKey, nil, opts...)
252+
if err != nil {
253+
return llb.Scratch(), err
244254
}
255+
256+
runOpts = append(runOpts, nativeGoMount(native, platform))
245257
}
246258

247259
specPath := filepath.Join("SPECS", spec.Name, spec.Name+".spec")

frontend/azlinux/handler.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ import (
1414
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
1515
)
1616

17-
const (
18-
tdnfCacheDir = "/var/cache/tdnf"
19-
)
20-
2117
type worker interface {
2218
Base(sOpt dalec.SourceOpts, opts ...llb.ConstraintsOpt) (llb.State, error)
2319
Install(pkgs []string, opts ...installOpt) llb.RunOption

frontend/azlinux/install.go

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ import (
77

88
"github.com/Azure/dalec"
99
"github.com/moby/buildkit/client/llb"
10+
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
11+
)
12+
13+
const (
14+
tdnfCacheDir = "/var/cache/tdnf"
15+
dnfCacheDir = "/var/cache/dnf"
1016
)
1117

1218
type installConfig struct {
@@ -23,7 +29,14 @@ type installConfig struct {
2329
// Additional mounts to add to the tdnf install command (useful if installing RPMS which are mounted to a local directory)
2430
mounts []llb.RunOption
2531

32+
// Instructs the installer to install packages for the specified platform
33+
platform *ocispecs.Platform
34+
2635
constraints []llb.ConstraintsOpt
36+
37+
// This forces the use of tdnf
38+
// Note this will almost certainly not work when platform is set.
39+
tdnfOnly bool
2740
}
2841

2942
type installOpt func(*installConfig)
@@ -32,6 +45,10 @@ func noGPGCheck(cfg *installConfig) {
3245
cfg.noGPGCheck = true
3346
}
3447

48+
func tdnfOnly(cfg *installConfig) {
49+
cfg.tdnfOnly = true
50+
}
51+
3552
func withMounts(opts ...llb.RunOption) installOpt {
3653
return func(cfg *installConfig) {
3754
cfg.mounts = append(cfg.mounts, opts...)
@@ -48,13 +65,19 @@ func atRoot(root string) installOpt {
4865
}
4966
}
5067

68+
func withPlatform(p *ocispecs.Platform) installOpt {
69+
return func(cfg *installConfig) {
70+
cfg.platform = p
71+
}
72+
}
73+
5174
func installWithConstraints(opts []llb.ConstraintsOpt) installOpt {
5275
return func(cfg *installConfig) {
5376
cfg.constraints = opts
5477
}
5578
}
5679

57-
func tdnfInstallFlags(cfg *installConfig) string {
80+
func dnfInstallFlags(cfg *installConfig) string {
5881
var cmdOpts string
5982

6083
if cfg.noGPGCheck {
@@ -63,12 +86,30 @@ func tdnfInstallFlags(cfg *installConfig) string {
6386

6487
if cfg.root != "" {
6588
cmdOpts += " --installroot=" + cfg.root
66-
cmdOpts += " --setopt=reposdir=/etc/yum.repos.d"
89+
cmdOpts += " --setopt reposdir=/etc/yum.repos.d"
90+
}
91+
92+
if cfg.platform != nil {
93+
// cmdOpts += " --ignorearch=true"
94+
cmdOpts += " --forcearch=" + ociArchToOS(cfg.platform)
6795
}
6896

6997
return cmdOpts
7098
}
7199

100+
func ociArchToOS(p *ocispecs.Platform) string {
101+
switch p.Architecture {
102+
case "amd64":
103+
return "x86_64"
104+
case "arm64":
105+
return "aarch64"
106+
// azlinux only supports amd64 and arm64
107+
// We shouldn't need any other arches.
108+
default:
109+
return p.Architecture
110+
}
111+
}
112+
72113
func setInstallOptions(cfg *installConfig, opts []installOpt) {
73114
for _, o := range opts {
74115
o(cfg)
@@ -111,9 +152,14 @@ rm -rf `+rpmdbDir+`
111152

112153
const manifestSh = "manifest.sh"
113154

114-
func tdnfInstall(cfg *installConfig, relVer string, pkgs []string) llb.RunOption {
115-
cmdFlags := tdnfInstallFlags(cfg)
116-
cmdArgs := fmt.Sprintf("set -ex; tdnf install -y --refresh --releasever=%s %s %s", relVer, cmdFlags, strings.Join(pkgs, " "))
155+
func dnfInstall(cfg *installConfig, relVer string, pkgs []string, cachePrefix string) llb.RunOption {
156+
cmdFlags := dnfInstallFlags(cfg)
157+
158+
cmd := "dnf"
159+
if cfg.tdnfOnly {
160+
cmd = "tdnf"
161+
}
162+
cmdArgs := fmt.Sprintf("set -ex; %s install -y --refresh --releasever=%s %s %s", cmd, relVer, cmdFlags, strings.Join(pkgs, " "))
117163

118164
var runOpts []llb.RunOption
119165

@@ -128,6 +174,7 @@ func tdnfInstall(cfg *installConfig, relVer string, pkgs []string) llb.RunOption
128174

129175
runOpts = append(runOpts, dalec.ShArgs(cmdArgs))
130176
runOpts = append(runOpts, cfg.mounts...)
177+
runOpts = append(runOpts, llb.AddMount("/var/cache/"+cmd, llb.Scratch(), llb.AsPersistentCacheDir(cachePrefix+"-"+cmd+"-"+"cache", llb.CacheMountLocked)))
131178

132179
return dalec.WithRunOptions(runOpts...)
133180
}

frontend/azlinux/mariner2.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package azlinux
33
import (
44
"context"
55
"encoding/json"
6-
"path/filepath"
76

87
"github.com/Azure/dalec"
98
"github.com/moby/buildkit/client/llb"
@@ -13,8 +12,7 @@ import (
1312
)
1413

1514
const (
16-
Mariner2TargetKey = "mariner2"
17-
tdnfCacheNameMariner2 = "mariner2-tdnf-cache"
15+
Mariner2TargetKey = "mariner2"
1816

1917
Mariner2Ref = "mcr.microsoft.com/cbl-mariner/base/core:2.0"
2018
Mariner2WorkerContextName = "dalec-mariner2-worker"
@@ -46,6 +44,8 @@ func (w mariner2) Base(sOpt dalec.SourceOpts, opts ...llb.ConstraintsOpt) (llb.S
4644
}
4745

4846
return base.Run(
47+
w.Install([]string{"dnf"}, installWithConstraints(opts), tdnfOnly),
48+
).Run(
4949
w.Install([]string{"rpm-build", "mariner-rpm-macros", "build-essential", "ca-certificates"}, installWithConstraints(opts)),
5050
dalec.WithConstraints(opts...),
5151
).Root(), nil
@@ -54,7 +54,7 @@ func (w mariner2) Base(sOpt dalec.SourceOpts, opts ...llb.ConstraintsOpt) (llb.S
5454
func (w mariner2) Install(pkgs []string, opts ...installOpt) llb.RunOption {
5555
var cfg installConfig
5656
setInstallOptions(&cfg, opts)
57-
return dalec.WithRunOptions(tdnfInstall(&cfg, "2.0", pkgs), w.tdnfCacheMount(cfg.root))
57+
return dalec.WithRunOptions(dnfInstall(&cfg, "2.0", pkgs, Mariner2TargetKey))
5858
}
5959

6060
func (w mariner2) BasePackages() []string {
@@ -90,7 +90,3 @@ func (mariner2) WorkerImageConfig(ctx context.Context, resolver llb.ImageMetaRes
9090

9191
return &cfg, nil
9292
}
93-
94-
func (mariner2) tdnfCacheMount(root string) llb.RunOption {
95-
return llb.AddMount(filepath.Join(root, tdnfCacheDir), llb.Scratch(), llb.AsPersistentCacheDir(tdnfCacheNameMariner2, llb.CacheMountLocked))
96-
}

0 commit comments

Comments
 (0)