Skip to content

Commit

Permalink
Run package spec tests for packaging targets (#397)
Browse files Browse the repository at this point in the history
Before this the package tests were only run when building a container
and not when just building a package.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
  • Loading branch information
cpuguy83 authored Oct 11, 2024
1 parent e9f1696 commit 7bb2748
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 163 deletions.
118 changes: 10 additions & 108 deletions frontend/azlinux/handle_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,112 +30,18 @@ func handleContainer(w worker) gwclient.BuildFunc {
return nil, nil, fmt.Errorf("error creating rpm: %w", err)
}

rpms, err := readRPMs(ctx, client, rpmDir)
if err != nil {
return nil, nil, err
}

st, err := specToContainerLLB(w, spec, targetKey, rpmDir, rpms, sOpt, pg)
if err != nil {
return nil, nil, err
}

def, err := st.Marshal(ctx, pg)
if err != nil {
return nil, nil, fmt.Errorf("error marshalling llb: %w", err)
}

res, err := client.Solve(ctx, gwclient.SolveRequest{
Definition: def.ToPB(),
})
if err != nil {
return nil, nil, err
}

img, err := resolveBaseConfig(ctx, w, client, platform, spec, targetKey)
if err != nil {
return nil, nil, errors.Wrap(err, "could not resolve base image config")
}

ref, err := res.SingleRef()
if err != nil {
return nil, nil, err
}

base, err := w.Base(sOpt, pg)
if err != nil {
return nil, nil, err
}

withTestDeps := func(in llb.State) llb.State {
deps := spec.GetTestDeps(targetKey)
if len(deps) == 0 {
return in
}
return base.Run(
w.Install(spec.GetTestDeps(targetKey), atRoot("/tmp/rootfs")),
pg,
dalec.ProgressGroup("Install test dependencies"),
).AddMount("/tmp/rootfs", in)
}

if err := frontend.RunTests(ctx, client, spec, ref, withTestDeps, targetKey); err != nil {
return nil, nil, err
}

ref, err := runTests(ctx, client, w, spec, sOpt, rpmDir, targetKey)
return ref, img, err
})
}
}

func readRPMs(ctx context.Context, client gwclient.Client, st llb.State) ([]string, error) {
def, err := st.Marshal(ctx)
if err != nil {
return nil, err
}

res, err := client.Solve(ctx, gwclient.SolveRequest{
Definition: def.ToPB(),
})
if err != nil {
return nil, err
}

ref, err := res.SingleRef()
if err != nil {
return nil, err
}

// Directory layout will have arch-specific sub-folders and/or `noarch`
// RPMs will be in those subdirectories.
arches, err := ref.ReadDir(ctx, gwclient.ReadDirRequest{
Path: "/RPMS",
})
if err != nil {
return nil, errors.Wrap(err, "error reading output state")
}

var out []string

for _, arch := range arches {
files, err := ref.ReadDir(ctx, gwclient.ReadDirRequest{
Path: filepath.Join("/RPMS", arch.Path),
IncludePattern: "*.rpm",
})

if err != nil {
return nil, errors.Wrap(err, "could not read arch specific output dir")
}

for _, e := range files {
out = append(out, filepath.Join(arch.Path, e.Path))
}
}

return out, nil
}

func specToContainerLLB(w worker, spec *dalec.Spec, targetKey string, rpmDir llb.State, files []string, sOpt dalec.SourceOpts, opts ...llb.ConstraintsOpt) (llb.State, error) {
func specToContainerLLB(w worker, spec *dalec.Spec, targetKey string, rpmDir llb.State, sOpt dalec.SourceOpts, opts ...llb.ConstraintsOpt) (llb.State, error) {
opts = append(opts, dalec.ProgressGroup("Install RPMs"))
const workPath = "/tmp/rootfs"

Expand All @@ -149,19 +55,15 @@ func specToContainerLLB(w worker, spec *dalec.Spec, targetKey string, rpmDir llb
rootfs = llb.Image(ref, llb.WithMetaResolver(sOpt.Resolver), dalec.WithConstraints(opts...))
}

if len(files) > 0 {
rpmMountDir := "/tmp/rpms"
updated := w.BasePackages()
for _, f := range files {
updated = append(updated, filepath.Join(rpmMountDir, f))
}
rpmMountDir := "/tmp/rpms"
pkgs := w.BasePackages()
pkgs = append(pkgs, filepath.Join(rpmMountDir, "**/*.rpm"))

rootfs = builderImg.Run(
w.Install(updated, atRoot(workPath), noGPGCheck, withManifests, installWithConstraints(opts)),
llb.AddMount(rpmMountDir, rpmDir, llb.SourcePath("/RPMS")),
dalec.WithConstraints(opts...),
).AddMount(workPath, rootfs)
}
rootfs = builderImg.Run(
w.Install(pkgs, atRoot(workPath), noGPGCheck, withManifests, installWithConstraints(opts)),
llb.AddMount(rpmMountDir, rpmDir, llb.SourcePath("/RPMS")),
dalec.WithConstraints(opts...),
).AddMount(workPath, rootfs)

if post := spec.GetImagePost(targetKey); post != nil && len(post.Symlinks) > 0 {
rootfs = builderImg.
Expand Down
7 changes: 1 addition & 6 deletions frontend/azlinux/handle_depsonly.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,7 @@ func handleDepsOnly(w worker) gwclient.BuildFunc {
).
AddMount("/tmp/rpms", llb.Scratch())

files, err := readRPMs(ctx, client, rpmDir)
if err != nil {
return nil, nil, err
}

st, err := specToContainerLLB(w, spec, targetKey, rpmDir, files, sOpt, pg)
st, err := specToContainerLLB(w, spec, targetKey, rpmDir, sOpt, pg)
if err != nil {
return nil, nil, err
}
Expand Down
60 changes: 60 additions & 0 deletions frontend/azlinux/handle_rpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -47,11 +48,70 @@ func handleRPM(w worker) gwclient.BuildFunc {
if err != nil {
return nil, nil, err
}

if imgRef, err := runTests(ctx, client, w, spec, sOpt, st, targetKey, pg); err != nil {
// return the container ref in case of error so it can be used to debug
// the installed package state.
cfg, _ := resolveBaseConfig(ctx, w, client, platform, spec, targetKey)
return imgRef, cfg, err
}

return ref, &dalec.DockerImageSpec{}, nil
})
}
}

// runTests runs the package tests
// The returned reference is the solved container state
func runTests(ctx context.Context, client gwclient.Client, w worker, spec *dalec.Spec, sOpt dalec.SourceOpts, rpmDir llb.State, targetKey string, opts ...llb.ConstraintsOpt) (gwclient.Reference, error) {
withDeps, err := withTestDeps(w, spec, sOpt, targetKey)
if err != nil {
return nil, err
}

imgSt, err := specToContainerLLB(w, spec, targetKey, rpmDir, sOpt, opts...)
if err != nil {
return nil, errors.Wrap(err, "error creating container image state")
}

def, err := imgSt.Marshal(ctx, opts...)
if err != nil {
return nil, err
}

res, err := client.Solve(ctx, gwclient.SolveRequest{Definition: def.ToPB()})
if err != nil {
return nil, errors.Wrap(err, "error solving container state")
}

ref, err := res.SingleRef()
if err != nil {
return nil, err
}

err = frontend.RunTests(ctx, client, spec, ref, withDeps, targetKey)
return ref, errors.Wrap(err, "TESTS FAILED")
}

func withTestDeps(w worker, spec *dalec.Spec, sOpt dalec.SourceOpts, targetKey string, opts ...llb.ConstraintsOpt) (llb.StateOption, error) {
base, err := w.Base(sOpt, opts...)
if err != nil {
return nil, err
}
return func(in llb.State) llb.State {
deps := spec.GetTestDeps(targetKey)
if len(deps) == 0 {
return in
}
return base.Run(
w.Install(spec.GetTestDeps(targetKey), atRoot("/tmp/rootfs")),
dalec.WithConstraints(opts...),
dalec.ProgressGroup("Install test dependencies"),
).AddMount("/tmp/rootfs", in)

}, nil
}

// 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 {
// depsOnly is a simple dalec spec that only includes build dependencies and their constraints
Expand Down
49 changes: 1 addition & 48 deletions frontend/jammy/handle_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@ package jammy
import (
"context"
"encoding/json"
"io/fs"
"strings"

"github.com/Azure/dalec"
"github.com/Azure/dalec/frontend"
"github.com/Azure/dalec/frontend/pkg/bkfs"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/client/llb/sourceresolver"
gwclient "github.com/moby/buildkit/frontend/gateway/client"
Expand Down Expand Up @@ -37,57 +35,12 @@ func handleContainer(ctx context.Context, client gwclient.Client) (*gwclient.Res
return nil, nil, err
}

worker, err := workerBase(sOpt, opt)
if err != nil {
return nil, nil, err
}

var includeTestRepo bool

workerFS, err := bkfs.FromState(ctx, &worker, client)
if err != nil {
return nil, nil, err
}

// Check if there there is a test repo in the worker image.
// We'll mount that into the target container while installing packages.
_, repoErr := fs.Stat(workerFS, testRepoPath[1:])
_, listErr := fs.Stat(workerFS, testRepoSourceListPath[1:])
if listErr == nil && repoErr == nil {
// This is a test and we need to include the repo from the worker image
// into target container.
includeTestRepo = true
frontend.Warn(ctx, client, worker, "Including test repo from worker image")
}

st := buildImageRootfs(worker, spec, sOpt, deb, targetKey, includeTestRepo, opt)

def, err := st.Marshal(ctx)
if err != nil {
return nil, nil, err
}

img, err := buildImageConfig(ctx, client, spec, platform, targetKey)
if err != nil {
return nil, nil, err
}

res, err := client.Solve(ctx, gwclient.SolveRequest{
Definition: def.ToPB(),
})
if err != nil {
return nil, nil, err
}

ref, err := res.SingleRef()
if err != nil {
return nil, nil, err
}

if err := frontend.RunTests(ctx, client, spec, ref, installTestDeps(spec, targetKey, opt), targetKey); err != nil {
return nil, nil, err
}

ref, err := runTests(ctx, client, spec, sOpt, deb, targetKey, opt)
return ref, img, err
})
}
Expand Down
58 changes: 57 additions & 1 deletion frontend/jammy/handle_deb.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package jammy
import (
"context"
"fmt"
"io/fs"
"strings"

"github.com/Azure/dalec"
"github.com/Azure/dalec/frontend"
"github.com/Azure/dalec/frontend/deb"
"github.com/Azure/dalec/frontend/pkg/bkfs"
"github.com/containerd/platforms"
"github.com/moby/buildkit/client/llb"
gwclient "github.com/moby/buildkit/frontend/gateway/client"
Expand All @@ -24,7 +26,8 @@ func handleDeb(ctx context.Context, client gwclient.Client) (*gwclient.Result, e
return nil, nil, err
}

st, err := buildDeb(ctx, client, spec, sOpt, targetKey, dalec.ProgressGroup("Building Jammy deb package: "+spec.Name))
opt := dalec.ProgressGroup("Building Jammy deb package: " + spec.Name)
st, err := buildDeb(ctx, client, spec, sOpt, targetKey, opt)
if err != nil {
return nil, nil, err
}
Expand All @@ -45,6 +48,12 @@ func handleDeb(ctx context.Context, client gwclient.Client) (*gwclient.Result, e
if err != nil {
return nil, nil, err
}

if ref, err := runTests(ctx, client, spec, sOpt, st, targetKey, opt); err != nil {
cfg, _ := buildImageConfig(ctx, client, spec, platform, targetKey)
return ref, cfg, err
}

if platform == nil {
p := platforms.DefaultSpec()
platform = &p
Expand All @@ -53,6 +62,53 @@ func handleDeb(ctx context.Context, client gwclient.Client) (*gwclient.Result, e
})
}

func runTests(ctx context.Context, client gwclient.Client, spec *dalec.Spec, sOpt dalec.SourceOpts, deb llb.State, targetKey string, opts ...llb.ConstraintsOpt) (gwclient.Reference, error) {
worker, err := workerBase(sOpt, opts...)
if err != nil {
return nil, err
}

var includeTestRepo bool

workerFS, err := bkfs.FromState(ctx, &worker, client)
if err != nil {
return nil, err
}

// Check if there there is a test repo in the worker image.
// We'll mount that into the target container while installing packages.
_, repoErr := fs.Stat(workerFS, testRepoPath[1:])
_, listErr := fs.Stat(workerFS, testRepoSourceListPath[1:])
if listErr == nil && repoErr == nil {
// This is a test and we need to include the repo from the worker image
// into target container.
includeTestRepo = true
frontend.Warn(ctx, client, worker, "Including test repo from worker image")
}

st := buildImageRootfs(worker, spec, sOpt, deb, targetKey, includeTestRepo, opts...)

def, err := st.Marshal(ctx, opts...)
if err != nil {
return nil, err
}

res, err := client.Solve(ctx, gwclient.SolveRequest{
Definition: def.ToPB(),
})
if err != nil {
return nil, err
}

ref, err := res.SingleRef()
if err != nil {
return nil, err
}

err = frontend.RunTests(ctx, client, spec, ref, installTestDeps(spec, targetKey, opts...), targetKey)
return ref, err
}

func installPackages(ls ...string) llb.RunOption {
return dalec.RunOptFunc(func(ei *llb.ExecInfo) {
// This only runs apt-get update if the pkgcache is older than 10 minutes.
Expand Down
Loading

0 comments on commit 7bb2748

Please sign in to comment.