diff --git a/frontend/azlinux/handle_container.go b/frontend/azlinux/handle_container.go index 29861828e..4284774d8 100644 --- a/frontend/azlinux/handle_container.go +++ b/frontend/azlinux/handle_container.go @@ -25,7 +25,7 @@ func handleContainer(w worker) gwclient.BuildFunc { pg := dalec.ProgressGroup("Building " + targetKey + " container: " + spec.Name) - rpmDir, err := specToRpmLLB(ctx, w, client, spec, sOpt, targetKey, pg, dalec.WithPlatform(platform)) + rpmDir, err := buildOutputRPM(ctx, w, client, spec, sOpt, targetKey, platform, pg) if err != nil { return nil, nil, fmt.Errorf("error creating rpm: %w", err) } diff --git a/frontend/azlinux/handle_depsonly.go b/frontend/azlinux/handle_depsonly.go index 93c2856d4..a7499f8b8 100644 --- a/frontend/azlinux/handle_depsonly.go +++ b/frontend/azlinux/handle_depsonly.go @@ -36,7 +36,7 @@ func handleDepsOnly(w worker) gwclient.BuildFunc { return nil, nil, err } - st, err := specToContainerLLB(w, spec, targetKey, rpmDir, files, sOpt, pg) + st, err := specToContainerLLB(w, spec, targetKey, rpmDir, files, sOpt, pg, dalec.WithPlatform(platform)) if err != nil { return nil, nil, err } diff --git a/frontend/azlinux/handle_rpm.go b/frontend/azlinux/handle_rpm.go index f54ead6ce..0c28861b2 100644 --- a/frontend/azlinux/handle_rpm.go +++ b/frontend/azlinux/handle_rpm.go @@ -11,6 +11,7 @@ import ( "github.com/moby/buildkit/client/llb" gwclient "github.com/moby/buildkit/frontend/gateway/client" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" ) func handleRPM(w worker) gwclient.BuildFunc { @@ -26,7 +27,7 @@ func handleRPM(w worker) gwclient.BuildFunc { return nil, nil, err } - st, err := specToRpmLLB(ctx, w, client, spec, sOpt, targetKey, pg, dalec.WithPlatform(platform)) + st, err := buildOutputRPM(ctx, w, client, spec, sOpt, targetKey, platform, pg) if err != nil { return nil, nil, err } @@ -53,7 +54,7 @@ func handleRPM(w worker) gwclient.BuildFunc { } // Creates and installs an rpm meta-package that requires the passed in deps as runtime-dependencies -func installBuildDepsPackage(target string, packageName string, w worker, deps map[string]dalec.PackageConstraints, installOpts ...installOpt) installFunc { +func installBuildDepsPackage(target string, packageName string, w worker, sOpt dalec.SourceOpts, deps map[string]dalec.PackageConstraints, platform *ocispecs.Platform, installOpts ...installOpt) installFunc { // depsOnly is a simple dalec spec that only includes build dependencies and their constraints depsOnly := dalec.Spec{ Name: fmt.Sprintf("%s-build-dependencies", packageName), @@ -66,11 +67,11 @@ func installBuildDepsPackage(target string, packageName string, w worker, deps m }, } - return func(ctx context.Context, client gwclient.Client, sOpt dalec.SourceOpts) (llb.RunOption, error) { + return func(ctx context.Context, Opt dalec.SourceOpts) (llb.RunOption, error) { pg := dalec.ProgressGroup("Building container for build dependencies") // create an RPM with just the build dependencies, using our same base worker - rpmDir, err := specToRpmLLB(ctx, w, client, &depsOnly, sOpt, target, pg) + rpmDir, err := createRPM(ctx, w, sOpt, &depsOnly, target, platform, pg) if err != nil { return nil, err } @@ -91,20 +92,15 @@ func installBuildDepsPackage(target string, packageName string, w worker, deps m } } -func installBuildDeps(ctx context.Context, w worker, client gwclient.Client, spec *dalec.Spec, targetKey string, opts ...llb.ConstraintsOpt) (llb.StateOption, error) { +func installBuildDeps(ctx context.Context, w worker, sOpt dalec.SourceOpts, spec *dalec.Spec, targetKey string, platform *ocispecs.Platform, opts ...llb.ConstraintsOpt) (llb.StateOption, error) { deps := spec.GetBuildDeps(targetKey) if len(deps) == 0 { return func(in llb.State) llb.State { return in }, nil } - sOpt, err := frontend.SourceOptFromClient(ctx, client) - if err != nil { - return nil, err - } - opts = append(opts, dalec.ProgressGroup("Install build deps")) - installOpt, err := installBuildDepsPackage(targetKey, spec.Name, w, deps, installWithConstraints(opts))(ctx, client, sOpt) + installOpt, err := installBuildDepsPackage(targetKey, spec.Name, w, sOpt, deps, platform, installWithConstraints(opts))(ctx, sOpt) if err != nil { return nil, err } @@ -114,24 +110,58 @@ func installBuildDeps(ctx context.Context, w worker, client gwclient.Client, spe }, nil } -func specToRpmLLB(ctx context.Context, w worker, client gwclient.Client, spec *dalec.Spec, sOpt dalec.SourceOpts, targetKey string, opts ...llb.ConstraintsOpt) (llb.State, error) { - base, err := w.Base(sOpt, opts...) +func createBuildroot(ctx context.Context, w worker, sOpt dalec.SourceOpts, spec *dalec.Spec, targetKey string, opts ...llb.ConstraintsOpt) (llb.State, error) { + opts = append(opts, dalec.ProgressGroup("Prepare rpm build root")) + + // Always generate the build root usign the native platform + // There is nothing it does that should require the requested target platform + native, err := w.Base(sOpt, opts...) if err != nil { return llb.Scratch(), err } - installOpt, err := installBuildDeps(ctx, w, client, spec, targetKey, opts...) + if spec.HasGomods() { + // Since the spec has go mods in it, we need to make sure we have go + // installed in the image. + nativeInstallDeps, err := installBuildDeps(ctx, w, sOpt, spec, targetKey, nil, opts...) + if err != nil { + return llb.Scratch(), err + } + + native = native.With(nativeInstallDeps) + } + + return rpm.SpecToBuildrootLLB(native, spec, sOpt, targetKey, opts...) +} + +func createRPM(ctx context.Context, w worker, sOpt dalec.SourceOpts, spec *dalec.Spec, targetKey string, platform *ocispecs.Platform, opts ...llb.ConstraintsOpt) (llb.State, error) { + br, err := createBuildroot(ctx, w, sOpt, spec, targetKey, opts...) + if err != nil { + return llb.Scratch(), errors.Wrap(err, "error creating rpm build root") + } + + specPath := filepath.Join("SPECS", spec.Name, spec.Name+".spec") + + // Build the RPM with the target platform + opts = append(opts, dalec.WithPlatform(platform)) + base, err := w.Base(sOpt, opts...) if err != nil { return llb.Scratch(), err } - base = base.With(installOpt) - br, err := rpm.SpecToBuildrootLLB(base, spec, sOpt, targetKey, opts...) + installDeps, err := installBuildDeps(ctx, w, sOpt, spec, targetKey, platform, opts...) if err != nil { return llb.Scratch(), err } - specPath := filepath.Join("SPECS", spec.Name, spec.Name+".spec") - st := rpm.Build(br, base, specPath, opts...) + base = base.With(installDeps) + return rpm.Build(br, base, specPath, opts...), nil +} + +func buildOutputRPM(ctx context.Context, w worker, client gwclient.Client, spec *dalec.Spec, sOpt dalec.SourceOpts, targetKey string, platform *ocispecs.Platform, opts ...llb.ConstraintsOpt) (llb.State, error) { + st, err := createRPM(ctx, w, sOpt, spec, targetKey, platform, opts...) + if err != nil { + return llb.Scratch(), err + } return frontend.MaybeSign(ctx, client, st, spec, targetKey, sOpt) } diff --git a/frontend/azlinux/handler.go b/frontend/azlinux/handler.go index dc2498f7e..9eb1ad361 100644 --- a/frontend/azlinux/handler.go +++ b/frontend/azlinux/handler.go @@ -18,7 +18,7 @@ const ( tdnfCacheDir = "/var/cache/tdnf" ) -type installFunc func(context.Context, gwclient.Client, dalec.SourceOpts) (llb.RunOption, error) +type installFunc func(context.Context, dalec.SourceOpts) (llb.RunOption, error) type worker interface { Base(sOpt dalec.SourceOpts, opts ...llb.ConstraintsOpt) (llb.State, error) @@ -62,12 +62,12 @@ func handleDebug(w worker) gwclient.BuildFunc { if err != nil { return nil, err } - return rpm.HandleDebug(getSpecWorker(ctx, w, client, sOpt))(ctx, client) + return rpm.HandleDebug(getSpecWorker(ctx, w, sOpt))(ctx, client) } } -func getSpecWorker(ctx context.Context, w worker, client gwclient.Client, sOpt dalec.SourceOpts) rpm.WorkerFunc { - return func(resolver llb.ImageMetaResolver, spec *dalec.Spec, targetKey string, opts ...llb.ConstraintsOpt) (llb.State, error) { +func getSpecWorker(ctx context.Context, w worker, sOpt dalec.SourceOpts) rpm.WorkerFunc { + return func(resolver llb.ImageMetaResolver, spec *dalec.Spec, targetKey string, platform *ocispecs.Platform, opts ...llb.ConstraintsOpt) (llb.State, error) { st, err := w.Base(sOpt, opts...) if err != nil { return llb.Scratch(), err @@ -83,7 +83,7 @@ func getSpecWorker(ctx context.Context, w worker, client gwclient.Client, sOpt d return llb.Scratch(), errors.New("spec contains go modules but does not have golang in build deps") } - installOpt, err := installBuildDeps(ctx, w, client, spec, targetKey, opts...) + installOpt, err := installBuildDeps(ctx, w, sOpt, spec, targetKey, platform, opts...) if err != nil { return llb.Scratch(), err } diff --git a/frontend/rpm/handle_buildroot.go b/frontend/rpm/handle_buildroot.go index 8dee177b1..c47b91eac 100644 --- a/frontend/rpm/handle_buildroot.go +++ b/frontend/rpm/handle_buildroot.go @@ -11,7 +11,7 @@ import ( ocispecs "github.com/opencontainers/image-spec/specs-go/v1" ) -type WorkerFunc func(resolver llb.ImageMetaResolver, spec *dalec.Spec, targetKey string, opts ...llb.ConstraintsOpt) (llb.State, error) +type WorkerFunc func(resolver llb.ImageMetaResolver, spec *dalec.Spec, targetKey string, platform *ocispecs.Platform, opts ...llb.ConstraintsOpt) (llb.State, error) func HandleBuildroot(wf WorkerFunc) gwclient.BuildFunc { return func(ctx context.Context, client gwclient.Client) (*gwclient.Result, error) { @@ -21,7 +21,9 @@ func HandleBuildroot(wf WorkerFunc) gwclient.BuildFunc { return nil, nil, err } - worker, err := wf(sOpt.Resolver, spec, targetKey) + // Note, we are not passing platform down here because everything should + // be able to work regardless of platform, so prefer the native platform. + worker, err := wf(sOpt.Resolver, spec, targetKey, platform) if err != nil { return nil, nil, err } diff --git a/frontend/rpm/handle_sources.go b/frontend/rpm/handle_sources.go index d46ddacc8..181166975 100644 --- a/frontend/rpm/handle_sources.go +++ b/frontend/rpm/handle_sources.go @@ -21,11 +21,13 @@ func HandleSources(wf WorkerFunc) gwclient.BuildFunc { return nil, nil, err } - worker, err := wf(sOpt.Resolver, spec, targetKey) + worker, err := wf(sOpt.Resolver, spec, targetKey, platform) if err != nil { return nil, nil, err } + // Note, we are not passing platform down here because everything should + // be able to work regardless of platform, so prefer the native platform. sources, err := Dalec2SourcesLLB(worker, spec, sOpt) if err != nil { return nil, nil, err