Skip to content

Commit 7e8b53b

Browse files
committed
reworked package recording so that it takes the parent image filesystem into account
1 parent 9036b5a commit 7e8b53b

29 files changed

Lines changed: 657 additions & 392 deletions

cmd/build.go

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"os"
77
"path/filepath"
88
"strings"
9+
"time"
910

1011
"chainguard.dev/apko/pkg/apk/fs"
1112
"github.com/Snakdy/container-build-engine/pkg/builder"
@@ -48,7 +49,8 @@ const (
4849
flagCacheDir = "cache-dir"
4950
flagPlatform = "platform"
5051

51-
flagSkipCACerts = "skip-ca-certificates"
52+
flagSkipCACerts = "skip-ca-certificates"
53+
flagSkipPackageRecording = "skip-package-recording"
5254
)
5355

5456
const (
@@ -70,6 +72,7 @@ func init() {
7072
buildCmd.Flags().String(flagPlatform, "linux/amd64", "build platform")
7173

7274
buildCmd.Flags().Bool(flagSkipCACerts, false, "skip running update-ca-certificates")
75+
buildCmd.Flags().Bool(flagSkipPackageRecording, true, "skip package recording")
7376

7477
_ = buildCmd.MarkFlagRequired(flagConfig)
7578
_ = buildCmd.MarkFlagFilename(flagConfig, ".yaml", ".yml")
@@ -142,19 +145,56 @@ func build(cmd *cobra.Command, _ []string) error {
142145
_ = os.Chdir(wd)
143146
log.Info("updating working directory", "dir", wd)
144147

145-
rootfs := fs.NewMemFS()
148+
// figure out what the uid should be
149+
uid := cfg.Spec.User.Uid
150+
if uid <= 0 && forceUid > 0 && forceUid != defaultUid {
151+
uid = forceUid
152+
} else if uid <= 0 {
153+
uid = defaultUid
154+
}
155+
156+
log.Info("preparing to build image", "username", username, "uid", uid, "dirfs", cfg.Spec.DirFS)
157+
var filesystem fs.FullFS
158+
if cfg.Spec.DirFS {
159+
tmpFs, err := os.MkdirTemp("", "container-build-engine-fs-*")
160+
if err != nil {
161+
log.Error(err, "failed to setup tmpfs")
162+
return err
163+
}
164+
filesystem = vfs.NewVFS(tmpFs)
165+
} else {
166+
filesystem = fs.NewMemFS()
167+
}
146168
log.V(3).Info("prepared root filesystem")
147169

170+
baseImage := airutil.ExpandEnv(lockFile.Packages[""].Resolved)
171+
switch baseImage {
172+
case containers.MagicImageScratch:
173+
case "":
174+
log.Info("using scratch base as nothing was provided")
175+
baseImage = containers.MagicImageScratch
176+
default:
177+
baseImage = airutil.ExpandEnv(cfg.Spec.From)
178+
}
179+
180+
// pull the base image
181+
pullStart := time.Now()
182+
baseImg, err := containers.Get(cmd.Context(), baseImage)
183+
if err != nil {
184+
return err
185+
}
186+
log.Info("pulled base image", "duration", time.Since(pullStart))
187+
148188
dl, err := downloader.NewDownloader(cacheDir)
149189
if err != nil {
150190
return err
151191
}
152192

153-
alpineKeeper, err := alpine.NewPackageKeeper(cmd.Context(), repoURLs(cfg.Spec.Repositories[strings.ToLower(string(aybv1.PackageAlpine))]), rootfs)
193+
alpineKeeper, err := alpine.NewPackageKeeper(cmd.Context(), repoURLs(cfg.Spec.Repositories[strings.ToLower(string(aybv1.PackageAlpine))]), filesystem, baseImg)
154194
if err != nil {
155195
return err
156196
}
157-
debianKeeper, err := debian.NewPackageKeeper(cmd.Context(), repoURLs(cfg.Spec.Repositories[strings.ToLower(string(aybv1.PackageDebian))]))
197+
debianKeeper, err := debian.NewPackageKeeper(cmd.Context(), repoURLs(cfg.Spec.Repositories[strings.ToLower(string(aybv1.PackageDebian))]), filesystem, baseImg)
158198
if err != nil {
159199
return err
160200
}
@@ -197,21 +237,6 @@ func build(cmd *cobra.Command, _ []string) error {
197237
pkgDeps = append(pkgDeps, id)
198238
}
199239

200-
baseImage := airutil.ExpandEnv(lockFile.Packages[""].Resolved)
201-
switch baseImage {
202-
case containers.MagicImageScratch:
203-
case "":
204-
log.Info("using scratch base as nothing was provided")
205-
baseImage = containers.MagicImageScratch
206-
default:
207-
baseImage = airutil.ExpandEnv(cfg.Spec.From)
208-
}
209-
210-
// pull the base image
211-
baseImg, err := containers.Get(cmd.Context(), baseImage)
212-
if err != nil {
213-
return err
214-
}
215240
imgCfg, err := baseImg.ConfigFile()
216241
if err != nil {
217242
return err
@@ -300,7 +325,7 @@ func build(cmd *cobra.Command, _ []string) error {
300325

301326
// update ca certificates
302327
if !skipCaCerts {
303-
if err := cacertificates.UpdateCertificates(cmd.Context(), rootfs); err != nil {
328+
if err := cacertificates.UpdateCertificates(cmd.Context(), filesystem); err != nil {
304329
return err
305330
}
306331
}
@@ -310,28 +335,7 @@ func build(cmd *cobra.Command, _ []string) error {
310335
entrypoint = []string{"/bin/sh"}
311336
}
312337

313-
// figure out what the uid should be
314-
uid := cfg.Spec.User.Uid
315-
if uid <= 0 && forceUid > 0 && forceUid != defaultUid {
316-
uid = forceUid
317-
} else if uid <= 0 {
318-
uid = defaultUid
319-
}
320-
321338
// package everything up as our final container image
322-
log.Info("preparing to build image", "username", username, "uid", uid, "dirfs", cfg.Spec.DirFS)
323-
var filesystem fs.FullFS
324-
if cfg.Spec.DirFS {
325-
tmpFs, err := os.MkdirTemp("", "container-build-engine-fs-*")
326-
if err != nil {
327-
log.Error(err, "failed to setup tmpfs")
328-
return err
329-
}
330-
filesystem = vfs.NewVFS(tmpFs)
331-
} else {
332-
filesystem = fs.NewMemFS()
333-
}
334-
335339
imageBuilder, err := builder.NewBuilder(cmd.Context(), baseImage, pipelineStatements, builder.Options{
336340
Username: username,
337341
Uid: uid,

cmd/lock.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,11 @@ func lock(cmd *cobra.Command, _ []string) error {
109109
}
110110
}
111111

112-
alpineKeeper, err := alpine.NewPackageKeeper(cmd.Context(), repoURLs(cfg.Spec.Repositories[strings.ToLower(string(aybv1.PackageAlpine))]), fs.NewMemFS())
112+
alpineKeeper, err := alpine.NewPackageKeeper(cmd.Context(), repoURLs(cfg.Spec.Repositories[strings.ToLower(string(aybv1.PackageAlpine))]), fs.NewMemFS(), nil)
113113
if err != nil {
114114
return err
115115
}
116-
debianKeeper, err := debian.NewPackageKeeper(cmd.Context(), repoURLs(cfg.Spec.Repositories[strings.ToLower(string(aybv1.PackageDebian))]))
116+
debianKeeper, err := debian.NewPackageKeeper(cmd.Context(), repoURLs(cfg.Spec.Repositories[strings.ToLower(string(aybv1.PackageDebian))]), fs.NewMemFS(), nil)
117117
if err != nil {
118118
return err
119119
}

internal/containerutil/push.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"crypto/x509"
66
"fmt"
77
"net/http"
8+
"time"
89

910
"github.com/Snakdy/container-build-engine/pkg/oci/auth"
1011
"github.com/go-logr/logr"
@@ -20,6 +21,8 @@ func Push(ctx context.Context, img v1.Image, dst string, certPool *x509.CertPool
2021
log := logr.FromContextOrDiscard(ctx).WithValues("ref", dst)
2122
log.Info("pushing image")
2223

24+
start := time.Now()
25+
2326
// tweak the default transport so that we
2427
// can provide a custom certPool
2528
transport := remote.DefaultTransport.(*http.Transport).Clone()
@@ -43,5 +46,7 @@ func Push(ctx context.Context, img v1.Image, dst string, certPool *x509.CertPool
4346
return err
4447
}
4548
fmt.Println(ref.String() + "@" + d.String())
49+
50+
log.Info("pushed image", "duration", time.Since(start))
4651
return nil
4752
}

internal/statements/package.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ func (s *PackageStatement) Run(ctx *pipelines.BuildContext, _ ...cbev1.Options)
9292
}
9393
}
9494

95+
if _, err := keeper.Resolve(ctx.Context, name); err != nil {
96+
log.Error(err, "failed to resolve package", "name", name, "version", version)
97+
return cbev1.Options{}, fmt.Errorf("resolving package details: %w", err)
98+
}
99+
95100
// unpack the package into the root
96101
// filesystem
97102
if err := keeper.Unpack(ctx.Context, pkgPath, ctx.FS); err != nil {

pkg/archiveutil/tar.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,48 @@ func Zuntar(ctx context.Context, r io.Reader, rootfs fs.FullFS) error {
4545
return Untar(ctx, zp, rootfs)
4646
}
4747

48+
// UntarFile extracts a single file from a given archive.
49+
func UntarFile(ctx context.Context, r io.Reader, filename string, rootfs fs.FullFS) error {
50+
log := logr.FromContextOrDiscard(ctx).WithValues("filename", filename)
51+
log.V(3).Info("searching tarball for file")
52+
tr := tar.NewReader(r)
53+
54+
for {
55+
header, err := tr.Next()
56+
switch {
57+
case err == io.EOF:
58+
return nil
59+
case err != nil:
60+
log.Error(err, "failed to read file from archive")
61+
return err
62+
case header == nil:
63+
continue
64+
}
65+
66+
target := filepath.Clean("/" + header.Name)
67+
68+
if header.Typeflag != tar.TypeReg || header.Name != filename {
69+
return nil
70+
}
71+
log.V(5).Info("creating file", "target", target, "mode", header.Mode)
72+
f, err := rootfs.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
73+
if err != nil {
74+
log.Error(err, "failed to open file", "target", target)
75+
return err
76+
}
77+
78+
if _, err := io.Copy(f, tr); err != nil {
79+
log.Error(err, "failed to extract file", "target", target)
80+
_ = f.Close()
81+
return err
82+
}
83+
_ = f.Close()
84+
85+
// now that we've found the file can bail out
86+
return nil
87+
}
88+
}
89+
4890
// Untar expands a tar archive into the given path.
4991
func Untar(ctx context.Context, r io.Reader, rootfs fs.FullFS) error {
5092
log := logr.FromContextOrDiscard(ctx)

pkg/archiveutil/tar_test.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package archiveutil
22

33
import (
4-
"chainguard.dev/apko/pkg/apk/fs"
54
"context"
5+
"os"
6+
"testing"
7+
8+
"chainguard.dev/apko/pkg/apk/fs"
69
"github.com/go-logr/logr"
710
"github.com/go-logr/logr/testr"
811
"github.com/stretchr/testify/assert"
912
"github.com/stretchr/testify/require"
10-
"os"
11-
"testing"
1213
)
1314

1415
func TestUntar(t *testing.T) {
@@ -29,3 +30,18 @@ func TestUntar(t *testing.T) {
2930
_, err = rootfs.Stat("test-symbolic.txt")
3031
assert.NotErrorIs(t, err, os.ErrNotExist)
3132
}
33+
34+
func TestUntarFile(t *testing.T) {
35+
ctx := logr.NewContext(context.TODO(), testr.NewWithOptions(t, testr.Options{Verbosity: 10}))
36+
37+
rootfs := fs.NewMemFS()
38+
39+
f, err := os.Open("./testdata/test.tar")
40+
require.NoError(t, err)
41+
defer f.Close()
42+
43+
assert.NoError(t, UntarFile(ctx, f, "test.txt", rootfs))
44+
45+
_, err = rootfs.Stat("test.txt")
46+
assert.NotErrorIs(t, err, os.ErrNotExist)
47+
}

pkg/packages/alpine/alpine.go

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,18 @@ import (
44
"context"
55
"net/http"
66
"os"
7-
"path/filepath"
87

98
"chainguard.dev/apko/pkg/apk/apk"
109
"chainguard.dev/apko/pkg/apk/fs"
10+
ociv1 "github.com/google/go-containerregistry/pkg/v1"
1111

1212
v1 "github.com/djcass44/all-your-base/pkg/api/v1"
1313
"github.com/djcass44/all-your-base/pkg/archiveutil"
1414
"github.com/djcass44/all-your-base/pkg/lockfile"
1515
"github.com/go-logr/logr"
1616
)
1717

18-
var installedFiles = []string{
19-
filepath.Join("/lib", "apk", "db", "installed"),
20-
filepath.Join("/usr", "lib", "apk", "db", "installed"),
21-
}
22-
23-
type PackageKeeper struct {
24-
rootfs fs.FullFS
25-
indices []apk.NamedIndex
26-
}
27-
28-
func NewPackageKeeper(ctx context.Context, repositories []string, rootfs fs.FullFS) (*PackageKeeper, error) {
18+
func NewPackageKeeper(ctx context.Context, repositories []string, rootfs fs.FullFS, base ociv1.Image) (*PackageKeeper, error) {
2919
log := logr.FromContextOrDiscard(ctx)
3020
indices, err := apk.GetRepositoryIndexes(ctx, repositories, map[string][]byte{}, "x86_64", apk.WithIgnoreSignatures(true), apk.WithHTTPClient(http.DefaultClient))
3121
if err != nil {
@@ -40,6 +30,7 @@ func NewPackageKeeper(ctx context.Context, repositories []string, rootfs fs.Full
4030
return &PackageKeeper{
4131
indices: indices,
4232
rootfs: rootfs,
33+
base: base,
4334
}, nil
4435
}
4536

@@ -60,19 +51,6 @@ func (*PackageKeeper) Unpack(ctx context.Context, pkg string, rootfs fs.FullFS)
6051
return nil
6152
}
6253

63-
func (p *PackageKeeper) Record(ctx context.Context, pkg *apk.RepositoryPackage, rootfs fs.FullFS) error {
64-
log := logr.FromContextOrDiscard(ctx).WithValues("pkg", pkg.Name)
65-
log.V(5).Info("recording package")
66-
67-
for _, i := range installedFiles {
68-
if err := p.writeInstalled(ctx, i, pkg, rootfs); err != nil {
69-
return err
70-
}
71-
}
72-
73-
return nil
74-
}
75-
7654
func (p *PackageKeeper) Resolve(ctx context.Context, pkg string) ([]lockfile.Package, error) {
7755
resolver := apk.NewPkgResolver(ctx, p.indices)
7856

@@ -83,7 +61,7 @@ func (p *PackageKeeper) Resolve(ctx context.Context, pkg string) ([]lockfile.Pac
8361
return nil, err
8462
}
8563

86-
if err := p.Record(ctx, repoPkg, p.rootfs); err != nil {
64+
if err := p.writeInstalled(ctx, append(repoPkgDeps, repoPkg), p.rootfs); err != nil {
8765
return nil, err
8866
}
8967

@@ -98,9 +76,6 @@ func (p *PackageKeeper) Resolve(ctx context.Context, pkg string) ([]lockfile.Pac
9876
Direct: true,
9977
}
10078
for i := range repoPkgDeps {
101-
if err := p.Record(ctx, repoPkgDeps[i], p.rootfs); err != nil {
102-
return nil, err
103-
}
10479
names[i+1] = lockfile.Package{
10580
Name: repoPkgDeps[i].Name,
10681
Resolved: repoPkgDeps[i].URL(),

pkg/packages/alpine/alpine_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"testing"
77

88
"chainguard.dev/apko/pkg/apk/fs"
9+
"github.com/Snakdy/container-build-engine/pkg/containers"
910
"github.com/djcass44/all-your-base/pkg/packages"
1011
"github.com/go-logr/logr"
1112
"github.com/go-logr/logr/testr"
@@ -31,10 +32,13 @@ func TestPackageKeeper_Unpack(t *testing.T) {
3132
}
3233

3334
func TestPackageKeeper_Resolve(t *testing.T) {
35+
ctx := logr.NewContext(context.TODO(), testr.NewWithOptions(t, testr.Options{Verbosity: 10}))
3436
testfs := fs.NewMemFS()
3537

36-
ctx := logr.NewContext(context.TODO(), testr.NewWithOptions(t, testr.Options{Verbosity: 10}))
37-
pkg, err := NewPackageKeeper(ctx, []string{"https://mirror.aarnet.edu.au/pub/alpine/v3.18/main"}, testfs)
38+
baseImage, err := containers.Get(ctx, "harbor.dcas.dev/docker.io/library/alpine:3.23")
39+
require.NoError(t, err)
40+
41+
pkg, err := NewPackageKeeper(ctx, []string{"https://mirror.aarnet.edu.au/pub/alpine/v3.23/main"}, testfs, baseImage)
3842
require.NoError(t, err)
3943

4044
packageNames, err := pkg.Resolve(ctx, "git")

0 commit comments

Comments
 (0)