Skip to content

Commit

Permalink
Use tarfs implementation for publish/build
Browse files Browse the repository at this point in the history
Actually opt into the optimized tarfs path.

This cuts the amount of time we spend on I/O dramatically.

Also, add a check that if a file from an APK conflicts with a file that
apko wrote (e.g. alpine keys conflicting with alpine-keys), ignore that
conflict if their checksums match.

Signed-off-by: Jon Johnson <jon.johnson@chainguard.dev>
  • Loading branch information
jonjohnsonjr committed Aug 31, 2023
1 parent 1654465 commit bec3e3c
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 14 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module chainguard.dev/apko

go 1.20

replace github.com/chainguard-dev/go-apk => ../go-apk

require (
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220920003936-cd2dbcbbab49
github.com/chainguard-dev/go-apk v0.0.0-20230830232041-24f27d33fced
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,6 @@ github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220920003936-
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/chainguard-dev/go-apk v0.0.0-20230830232041-24f27d33fced h1:3FPKsvRFzRwDJ58Gbxt7Cjiz8eec4rtlf4uEsBqX/LA=
github.com/chainguard-dev/go-apk v0.0.0-20230830232041-24f27d33fced/go.mod h1:I7uFl3LBMG1UQONJRQN+3lzZE4tw8myh87FzkDEztHg=
github.com/chrismellard/docker-credential-acr-env v0.0.0-20220327082430-c57b701bfc08 h1:9Qh4lJ/KMr5iS1zfZ8I97+3MDpiKjl+0lZVUNBhdvRs=
github.com/chrismellard/docker-credential-acr-env v0.0.0-20220327082430-c57b701bfc08/go.mod h1:MAuu1uDJNOS3T3ui0qmKdPUwm59+bO19BbTph2wZafE=
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
Expand Down
13 changes: 3 additions & 10 deletions internal/cli/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"sync"

"github.com/chainguard-dev/go-apk/pkg/apk"
apkfs "github.com/chainguard-dev/go-apk/pkg/fs"
coci "github.com/sigstore/cosign/v2/pkg/oci"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand All @@ -33,6 +32,7 @@ import (
"golang.org/x/sync/errgroup"
"golang.org/x/sys/unix"

"chainguard.dev/apko/internal/tarfs"
"chainguard.dev/apko/pkg/build"
"chainguard.dev/apko/pkg/build/oci"
"chainguard.dev/apko/pkg/build/types"
Expand Down Expand Up @@ -178,7 +178,7 @@ func BuildCmd(ctx context.Context, imageRef, outputTarGZ string, archs []types.A

// buildImage build all of the components of an image in a single working directory.
// Each layer is a separate file, as are config, manifests, index and sbom.
func buildImageComponents(ctx context.Context, wd string, archs []types.Architecture, opts ...build.Option) (idx coci.SignedImageIndex, sboms []types.SBOM, pkgs map[types.Architecture][]*apk.InstalledPackage, err error) {
func buildImageComponents(ctx context.Context, workDir string, archs []types.Architecture, opts ...build.Option) (idx coci.SignedImageIndex, sboms []types.SBOM, pkgs map[types.Architecture][]*apk.InstalledPackage, err error) {
ctx, span := otel.Tracer("apko").Start(ctx, "buildImageComponents")
defer span.End()

Expand Down Expand Up @@ -215,7 +215,6 @@ func buildImageComponents(ctx context.Context, wd string, archs []types.Architec
o.Logger().Printf("building tags %v", o.Tags)

var errg errgroup.Group
workDir := wd
imageDir := filepath.Join(workDir, "image")
if err := os.MkdirAll(imageDir, 0755); err != nil {
return nil, nil, nil, fmt.Errorf("unable to create working image directory %s: %w", imageDir, err)
Expand All @@ -238,23 +237,17 @@ func buildImageComponents(ctx context.Context, wd string, archs []types.Architec

for _, arch := range archs {
arch := arch
// working directory for this architecture
wd := filepath.Join(workDir, arch.ToAPK())
bopts := slices.Clone(opts)
bopts = append(bopts,
build.WithArch(arch),
build.WithSBOM(imageDir),
)

fs := apkfs.DirFS(wd, apkfs.WithCreateDir())

bc, err := build.New(ctx, fs, bopts...)
bc, err := build.New(ctx, tarfs.New(), bopts...)
if err != nil {
return nil, nil, nil, err
}

bc.Logger().Infof("using working directory %s", wd)

// save the build context for later
contexts[arch] = bc

Expand Down
19 changes: 17 additions & 2 deletions internal/tarfs/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package tarfs
import (
"archive/tar"
"bytes"
"crypto/sha1" //nolint:gosec // this is what apk tools is using
"encoding/base64"
"encoding/hex"
"errors"
Expand Down Expand Up @@ -465,8 +466,22 @@ func (m *memFS) writeHeader(name string, te tarEntry) error {
want, got := te, existing.te

if got == nil {
// This shouldn't happen.
return fmt.Errorf("conflicting file for %q has no tar entry", name)
if existing.data == nil {
return fmt.Errorf("conflicting file for %q has no tar entry", name)
}

// This can happen when go-apk's InitKeyring conflicts with alpine-keys.
// Since those files will be in memory, quickly compute the checksum and
// ignore this file if they match.
h := sha1.New() //nolint:gosec // this is what apk tools is using
h.Write(existing.data)
checksum := h.Sum(nil)

if bytes.Equal(want.checksum, checksum) {
return nil
}

return fmt.Errorf("conflicting file for %q with checksum %x, existing has checksum %x", name, want.checksum, checksum)
}

// Files have the same checksum, that's fine.
Expand Down

0 comments on commit bec3e3c

Please sign in to comment.