From 2014a34ab6a2d2eff95fdfee1370f302315f8dc4 Mon Sep 17 00:00:00 2001 From: Jon Johnson Date: Tue, 18 Jul 2023 13:14:49 -0700 Subject: [PATCH] Inline apk package (mostly) to use go-apk directly This was largely replaced by go-apk already, but there is a lot of vestigial indirection that doesn't really need to be there anymore. Since we're no longer reusing build.Context for multiple things, this drops Refesh() and moves a lot of initialization into build.New(). That initialization doesn't always make sense (e.g. when we build.New() just to get access to configuration), so this also adds a build.NewOptions() function that returns ImageConfiguration and Options for setting up the platform specific builds. This is a little bit silly, but we should be able to drop it once I've refactoring this a bit to have separate paths for multi-platform vs single platform. Signed-off-by: Jon Johnson --- internal/cli/build-minirootfs.go | 6 +- internal/cli/build.go | 44 ++++------ internal/cli/show-config.go | 6 +- internal/cli/show-packages.go | 12 +-- pkg/apk/apk.go | 140 ------------------------------ pkg/apk/apk_implementation.go | 15 ---- pkg/apk/const.go | 19 ---- pkg/build/apk.go | 77 ++++++++++++++++ pkg/build/build.go | 79 ++++++++++++----- pkg/build/build_implementation.go | 51 ++--------- pkg/build/busybox.go | 15 +--- pkg/build/busybox_test.go | 13 +-- 12 files changed, 175 insertions(+), 302 deletions(-) delete mode 100644 pkg/apk/apk_implementation.go delete mode 100644 pkg/apk/const.go create mode 100644 pkg/build/apk.go diff --git a/internal/cli/build-minirootfs.go b/internal/cli/build-minirootfs.go index 20dc40ed8..5a5b5c0c2 100644 --- a/internal/cli/build-minirootfs.go +++ b/internal/cli/build-minirootfs.go @@ -86,15 +86,11 @@ func BuildMinirootFSCmd(ctx context.Context, opts ...build.Option) error { } defer os.RemoveAll(wd) - bc, err := build.New(wd, opts...) + bc, err := build.New(ctx, wd, opts...) if err != nil { return err } - if err := bc.Refresh(); err != nil { - return err - } - ic := bc.ImageConfiguration() if len(ic.Archs) != 0 { diff --git a/internal/cli/build.go b/internal/cli/build.go index dccea0fc2..7883a2e1c 100644 --- a/internal/cli/build.go +++ b/internal/cli/build.go @@ -181,17 +181,11 @@ func buildImageComponents(ctx context.Context, wd string, archs []types.Architec ctx, span := otel.Tracer("apko").Start(ctx, "buildImageComponents") defer span.End() - bc, err := build.New(wd, opts...) + o, ic, err := build.NewOptions(wd, opts...) if err != nil { return nil, nil, err } - if err := bc.Refresh(); err != nil { - return nil, nil, err - } - - ic := bc.ImageConfiguration() - // cases: // - archs set: use those archs // - archs not set, bc.ImageConfiguration.Archs set: use Config archs @@ -206,7 +200,7 @@ func buildImageComponents(ctx context.Context, wd string, archs []types.Architec } // save the final set we will build archs = ic.Archs - bc.Logger().Infof("Building images for %d architectures: %+v", len(ic.Archs), ic.Archs) + o.Logger().Infof("Building images for %d architectures: %+v", len(ic.Archs), ic.Archs) // The build context options is sometimes copied in the next functions. Ensure // we have the directory defined and created by invoking the function early. @@ -217,7 +211,7 @@ func buildImageComponents(ctx context.Context, wd string, archs []types.Architec // image/ - the summary layer files and sboms for each architecture // imageDir, created here, is where the final artifacts will be: layer tars, indexes, etc. - bc.Logger().Printf("building tags %v", bc.Tags()) + o.Logger().Printf("building tags %v", o.Tags) var errg errgroup.Group workDir := wd @@ -236,7 +230,7 @@ func buildImageComponents(ctx context.Context, wd string, archs []types.Architec // maximum "build date epoch" of the per-arch images. If the user has // explicitly set SOURCE_DATE_EPOCH, that will always trump this // computation. - multiArchBDE := bc.SourceDateEpoch() + multiArchBDE := o.SourceDateEpoch for _, arch := range archs { arch := arch @@ -245,10 +239,9 @@ func buildImageComponents(ctx context.Context, wd string, archs []types.Architec bopts := slices.Clone(opts) bopts = append(bopts, build.WithArch(arch), - build.WithTarball(filepath.Join(imageDir, bc.TarballFilename())), build.WithSBOM(imageDir), ) - bc, err := build.New(wd, bopts...) + bc, err := build.New(ctx, wd, bopts...) if err != nil { return nil, nil, err } @@ -257,10 +250,6 @@ func buildImageComponents(ctx context.Context, wd string, archs []types.Architec contexts[arch] = bc errg.Go(func() error { - if err := bc.Refresh(); err != nil { - return fmt.Errorf("failed to update build context for %q: %w", arch, err) - } - layerTarGZ, layer, err := bc.BuildLayer(ctx) if err != nil { return fmt.Errorf("failed to build layer image for %q: %w", arch, err) @@ -301,10 +290,22 @@ func buildImageComponents(ctx context.Context, wd string, archs []types.Architec } // generate the index - finalDigest, idx, err := oci.GenerateIndex(ctx, ic, imgs) + finalDigest, idx, err := oci.GenerateIndex(ctx, *ic, imgs) if err != nil { return nil, nil, fmt.Errorf("failed to generate OCI index: %w", err) } + + opts = append(opts, + build.WithImageConfiguration(*ic), // We mutate Archs above. + build.WithSourceDateEpoch(multiArchBDE), // Maximum child's time. + build.WithSBOM(imageDir), + ) + + bc, err := build.New(ctx, wd, opts...) + if err != nil { + return nil, nil, err + } + if _, _, err := bc.WriteIndex(idx); err != nil { return nil, nil, fmt.Errorf("failed to write OCI index: %w", err) } @@ -337,15 +338,6 @@ func buildImageComponents(ctx context.Context, wd string, archs []types.Architec return nil, nil, err } - opts = append(opts, - build.WithImageConfiguration(ic), // We mutate Archs above. - build.WithSourceDateEpoch(multiArchBDE), // Maximum child's time. - build.WithSBOM(imageDir), - ) - bc, err := build.New(wd, opts...) - if err != nil { - return nil, nil, err - } files, err := bc.GenerateIndexSBOM(ctx, finalDigest, imgs) if err != nil { return nil, nil, fmt.Errorf("generating index SBOM: %w", err) diff --git a/internal/cli/show-config.go b/internal/cli/show-config.go index 64aada4f6..8c59316fc 100644 --- a/internal/cli/show-config.go +++ b/internal/cli/show-config.go @@ -62,15 +62,11 @@ func ShowConfigCmd(ctx context.Context, opts ...build.Option) error { } defer os.RemoveAll(wd) - bc, err := build.New(wd, opts...) + bc, err := build.New(ctx, wd, opts...) if err != nil { return err } - if err := bc.Refresh(); err != nil { - return err - } - var buf bytes.Buffer enc := yaml.NewEncoder(&buf) diff --git a/internal/cli/show-packages.go b/internal/cli/show-packages.go index af8feaa4c..b2fb03fcf 100644 --- a/internal/cli/show-packages.go +++ b/internal/cli/show-packages.go @@ -123,15 +123,11 @@ func ShowPackagesCmd(ctx context.Context, format string, archs []types.Architect } defer os.RemoveAll(wd) - bc, err := build.New(wd, opts...) + bc, err := build.New(ctx, wd, opts...) if err != nil { return err } - if err := bc.Refresh(); err != nil { - return err - } - ic := bc.ImageConfiguration() // cases: @@ -165,15 +161,11 @@ func ShowPackagesCmd(ctx context.Context, format string, archs []types.Architect wd := filepath.Join(wd, arch.ToAPK()) bopts := slices.Clone(opts) bopts = append(bopts, build.WithArch(arch)) - bc, err := build.New(wd, bopts...) + bc, err := build.New(ctx, wd, bopts...) if err != nil { return err } - if err := bc.Refresh(); err != nil { - return fmt.Errorf("failed to update build context for %q: %w", arch, err) - } - pkgs, _, err := bc.BuildPackageList(ctx) if err != nil { return fmt.Errorf("failed to get package list for image: %w", err) diff --git a/pkg/apk/apk.go b/pkg/apk/apk.go index f99fca672..3e0d8c6cd 100644 --- a/pkg/apk/apk.go +++ b/pkg/apk/apk.go @@ -17,140 +17,18 @@ package apk //go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate import ( - "archive/tar" - "context" "fmt" "io/fs" - "os" "regexp" "sort" "strings" - apkimpl "github.com/chainguard-dev/go-apk/pkg/apk" - apkfs "github.com/chainguard-dev/go-apk/pkg/fs" "github.com/google/go-containerregistry/pkg/name" - "gitlab.alpinelinux.org/alpine/go/repository" - "golang.org/x/sync/errgroup" - "k8s.io/apimachinery/pkg/util/sets" - "chainguard.dev/apko/pkg/build/types" "chainguard.dev/apko/pkg/options" "chainguard.dev/apko/pkg/sbom" ) -type APK struct { - impl *apkimpl.APK - fs apkfs.FullFS - Options options.Options -} - -func New() (*APK, error) { - return NewWithOptions(apkfs.DirFS("/"), options.Default) -} - -func NewWithOptions(fsys apkfs.FullFS, o options.Options) (*APK, error) { - opts := options.Default - if o.Log == nil { - o.Log = opts.Log - } - - // apko does not execute the scripts, so they do not matter. This buys us flexibility - // to run without root privileges, or even on non-Linux. - apkOpts := []apkimpl.Option{ - apkimpl.WithFS(fsys), - apkimpl.WithLogger(o.Logger()), - apkimpl.WithArch(o.Arch.ToAPK()), - apkimpl.WithIgnoreMknodErrors(true), - } - // only try to pass the cache dir if one of the following is true: - // - the user has explicitly set a cache dir - // - the user's system-determined cachedir, as set by os.UserCacheDir(), can be found - // if neither of these are true, then we don't want to pass a cache dir, because - // go-apk will try to set it to os.UserCacheDir() which returns an error if $HOME - // is not set. - - // note that this is not easy to do in a switch statement, because of the second - // condition, if err := ...; err == nil {} - if o.CacheDir != "" { - apkOpts = append(apkOpts, apkimpl.WithCache(o.CacheDir)) - } else if _, err := os.UserCacheDir(); err == nil { - apkOpts = append(apkOpts, apkimpl.WithCache(o.CacheDir)) - } else { - o.Logger().Warnf("cache disabled because cache dir was not set, and cannot determine system default: %v", err) - } - - apkImpl, err := apkimpl.New(apkOpts...) - if err != nil { - return nil, err - } - a := &APK{ - Options: o, - impl: apkImpl, - fs: fsys, - } - return a, nil -} - -type Option func(*APK) error - -// Initialize sets the image in Context.WorkDir according to the image configuration, -// and does everything short of installing the packages. -func (a *APK) Initialize(ctx context.Context, ic types.ImageConfiguration) error { - // initialize apk - alpineVersions := parseOptionsFromRepositories(ic.Contents.Repositories) - if err := a.impl.InitDB(ctx, alpineVersions...); err != nil { - return fmt.Errorf("failed to initialize apk database: %w", err) - } - - var eg errgroup.Group - - eg.Go(func() error { - keyring := sets.List(sets.New(ic.Contents.Keyring...).Insert(a.Options.ExtraKeyFiles...)) - if err := a.impl.InitKeyring(ctx, keyring, nil); err != nil { - return fmt.Errorf("failed to initialize apk keyring: %w", err) - } - return nil - }) - - eg.Go(func() error { - repos := sets.List(sets.New(ic.Contents.Repositories...).Insert(a.Options.ExtraRepos...)) - if err := a.impl.SetRepositories(repos); err != nil { - return fmt.Errorf("failed to initialize apk repositories: %w", err) - } - return nil - }) - - eg.Go(func() error { - packages := sets.List(sets.New(ic.Contents.Packages...).Insert(a.Options.ExtraPackages...)) - if err := a.impl.SetWorld(packages); err != nil { - return fmt.Errorf("failed to initialize apk world: %w", err) - } - return nil - }) - - if err := eg.Wait(); err != nil { - return err - } - - return nil -} - -// Install install packages. Only works if already initialized. -func (a *APK) Install(ctx context.Context) error { - // sync reality with desired apk world - return a.impl.FixateWorld(ctx, &a.Options.SourceDateEpoch) -} - -// ResolvePackages gets list of packages that should be installed -func (a *APK) ResolvePackages(ctx context.Context) (toInstall []*repository.RepositoryPackage, conflicts []string, err error) { - // sync reality with desired apk world - return a.impl.ResolveWorld(ctx) -} - -func (a *APK) GetInstalled() ([]*apkimpl.InstalledPackage, error) { - return a.impl.GetInstalled() -} - // AdditionalTags is a helper function used in conjunction with the --package-version-tag flag // If --package-version-tag is set to a package name (e.g. go), then this function // returns a list of all images that should be published with the associated version of that package tagged (e.g. 1.18) @@ -242,21 +120,3 @@ func getStemmedVersionTags(opts options.Options, origRef string, version string) }) return tags, nil } - -func (a *APK) ListInitFiles() []tar.Header { - return a.impl.ListInitFiles() -} - -var repoRE = regexp.MustCompile(`^http[s]?://.+\/alpine\/([^\/]+)\/[^\/]+$`) - -func parseOptionsFromRepositories(repos []string) []string { - var versions = make([]string, 0) - for _, r := range repos { - parts := repoRE.FindStringSubmatch(r) - if len(parts) < 2 { - continue - } - versions = append(versions, parts[1]) - } - return versions -} diff --git a/pkg/apk/apk_implementation.go b/pkg/apk/apk_implementation.go deleted file mode 100644 index 3a993b76b..000000000 --- a/pkg/apk/apk_implementation.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2022, 2023 Chainguard, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package apk diff --git a/pkg/apk/const.go b/pkg/apk/const.go deleted file mode 100644 index 5c9ce9886..000000000 --- a/pkg/apk/const.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2023 Chainguard, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package apk - -const ( - DefaultKeyRingPath = "/etc/apk/keys" - DefaultSystemKeyRingPath = "/usr/share/apk/keys/" -) diff --git a/pkg/build/apk.go b/pkg/build/apk.go new file mode 100644 index 000000000..1cfd10e79 --- /dev/null +++ b/pkg/build/apk.go @@ -0,0 +1,77 @@ +// Copyright 2022, 2023 Chainguard, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package build + +import ( + "context" + "fmt" + "regexp" + + "golang.org/x/sync/errgroup" + "k8s.io/apimachinery/pkg/util/sets" +) + +func (bc *Context) initializeApk(ctx context.Context) error { + alpineVersions := parseOptionsFromRepositories(bc.ic.Contents.Repositories) + if err := bc.apk.InitDB(ctx, alpineVersions...); err != nil { + return fmt.Errorf("failed to initialize apk database: %w", err) + } + + var eg errgroup.Group + + eg.Go(func() error { + keyring := sets.List(sets.New(bc.ic.Contents.Keyring...).Insert(bc.o.ExtraKeyFiles...)) + if err := bc.apk.InitKeyring(ctx, keyring, nil); err != nil { + return fmt.Errorf("failed to initialize apk keyring: %w", err) + } + return nil + }) + + eg.Go(func() error { + repos := sets.List(sets.New(bc.ic.Contents.Repositories...).Insert(bc.o.ExtraRepos...)) + if err := bc.apk.SetRepositories(repos); err != nil { + return fmt.Errorf("failed to initialize apk repositories: %w", err) + } + return nil + }) + + eg.Go(func() error { + packages := sets.List(sets.New(bc.ic.Contents.Packages...).Insert(bc.o.ExtraPackages...)) + if err := bc.apk.SetWorld(packages); err != nil { + return fmt.Errorf("failed to initialize apk world: %w", err) + } + return nil + }) + + if err := eg.Wait(); err != nil { + return err + } + + return nil +} + +var repoRE = regexp.MustCompile(`^http[s]?://.+\/alpine\/([^\/]+)\/[^\/]+$`) + +func parseOptionsFromRepositories(repos []string) []string { + var versions = make([]string, 0) + for _, r := range repos { + parts := repoRE.FindStringSubmatch(r) + if len(parts) < 2 { + continue + } + versions = append(versions, parts[1]) + } + return versions +} diff --git a/pkg/build/build.go b/pkg/build/build.go index ccea93376..0b8d1b54f 100644 --- a/pkg/build/build.go +++ b/pkg/build/build.go @@ -28,6 +28,7 @@ import ( "strings" "time" + "github.com/chainguard-dev/go-apk/pkg/apk" apkfs "github.com/chainguard-dev/go-apk/pkg/fs" v1 "github.com/google/go-containerregistry/pkg/v1" v1types "github.com/google/go-containerregistry/pkg/v1/types" @@ -56,6 +57,7 @@ type Context struct { s6 *s6.Context assertions []Assertion fs apkfs.FullFS + apk *apk.APK } func (bc *Context) Summarize() { @@ -68,7 +70,7 @@ func (bc *Context) GetBuildDateEpoch() (time.Time, error) { if _, ok := os.LookupEnv("SOURCE_DATE_EPOCH"); ok { return bc.o.SourceDateEpoch, nil } - pl, err := bc.InstalledPackages() + pl, err := bc.apk.GetInstalled() if err != nil { return time.Time{}, fmt.Errorf("failed to determine installed packages: %w", err) } @@ -175,10 +177,26 @@ func (bc *Context) runAssertions() error { return eg.Wait().ErrorOrNil() } +// NewOptions evaluates the build.Options in the same way as New(). +func NewOptions(workDir string, opts ...Option) (*options.Options, *types.ImageConfiguration, error) { + bc := Context{ + o: options.Default, + } + bc.o.WorkDir = workDir + + for _, opt := range opts { + if err := opt(&bc); err != nil { + return nil, nil, err + } + } + + return &bc.o, &bc.ic, nil +} + // New creates a build context. // The SOURCE_DATE_EPOCH env variable is supported and will // overwrite the provided timestamp if present. -func New(workDir string, opts ...Option) (*Context, error) { +func New(ctx context.Context, workDir string, opts ...Option) (*Context, error) { fs := apkfs.DirFS(workDir, apkfs.WithCreateDir()) bc := Context{ o: options.Default, @@ -217,16 +235,49 @@ func New(workDir string, opts ...Option) (*Context, error) { bc.ic.ProbeVCSUrl(bc.imageConfigFile, bc.Logger()) } - return &bc, nil -} + apkOpts := []apk.Option{ + apk.WithFS(bc.fs), + apk.WithLogger(bc.Logger()), + apk.WithArch(bc.o.Arch.ToAPK()), + apk.WithIgnoreMknodErrors(true), + } + // only try to pass the cache dir if one of the following is true: + // - the user has explicitly set a cache dir + // - the user's system-determined cachedir, as set by os.UserCacheDir(), can be found + // if neither of these are true, then we don't want to pass a cache dir, because + // go-apk will try to set it to os.UserCacheDir() which returns an error if $HOME + // is not set. + + // note that this is not easy to do in a switch statement, because of the second + // condition, if err := ...; err == nil {} + if bc.o.CacheDir != "" { + apkOpts = append(apkOpts, apk.WithCache(bc.o.CacheDir)) + } else if _, err := os.UserCacheDir(); err == nil { + apkOpts = append(apkOpts, apk.WithCache(bc.o.CacheDir)) + } else { + bc.Logger().Warnf("cache disabled because cache dir was not set, and cannot determine system default: %v", err) + } -// Refresh initializes the build process by creating a new s6 context. -func (bc *Context) Refresh() error { - bc.o.TarballPath = "" + apkImpl, err := apk.New(apkOpts...) + if err != nil { + return nil, err + } + + bc.apk = apkImpl + + bc.Logger().Infof("doing pre-flight checks") + if err := bc.ic.Validate(); err != nil { + return nil, fmt.Errorf("failed to validate configuration: %w", err) + } + + bc.Logger().Infof("building apk info in %s", bc.o.WorkDir) + if err := bc.initializeApk(ctx); err != nil { + return nil, fmt.Errorf("initializing apk: %w", err) + } bc.s6 = s6.New(bc.fs, bc.Logger()) - return nil + return &bc, nil } // layer implements v1.Layer from go-containerregistry to avoid re-computing @@ -290,18 +341,6 @@ func (bc *Context) Arch() types.Architecture { return bc.o.Arch } -func (bc *Context) Tags() []string { - return bc.o.Tags -} - -func (bc *Context) SourceDateEpoch() time.Time { - return bc.o.SourceDateEpoch -} - -func (bc *Context) TarballFilename() string { - return bc.o.TarballFileName() -} - func (bc *Context) WantSBOM() bool { return len(bc.o.SBOMFormats) != 0 } diff --git a/pkg/build/build_implementation.go b/pkg/build/build_implementation.go index 6a2c15871..3aa7af268 100644 --- a/pkg/build/build_implementation.go +++ b/pkg/build/build_implementation.go @@ -31,7 +31,6 @@ import ( gzip "github.com/klauspost/pgzip" "go.opentelemetry.io/otel" - apkimpl "github.com/chainguard-dev/go-apk/pkg/apk" apkfs "github.com/chainguard-dev/go-apk/pkg/fs" "github.com/chainguard-dev/go-apk/pkg/tarball" "github.com/google/go-containerregistry/pkg/name" @@ -166,14 +165,6 @@ func (bc *Context) GenerateImageSBOM(ctx context.Context, arch types.Architectur return sboms, nil } -func (bc *Context) InstalledPackages() ([]*apkimpl.InstalledPackage, error) { - apk, err := chainguardAPK.NewWithOptions(bc.fs, bc.o) - if err != nil { - return nil, err - } - return apk.GetInstalled() -} - func additionalTags(fsys apkfs.FullFS, o *options.Options) error { at, err := chainguardAPK.AdditionalTags(fsys, *o) if err != nil { @@ -190,23 +181,7 @@ func (bc *Context) buildImage(ctx context.Context) error { ctx, span := otel.Tracer("apko").Start(ctx, "buildImage") defer span.End() - bc.Logger().Infof("doing pre-flight checks") - if err := bc.ic.Validate(); err != nil { - return fmt.Errorf("failed to validate configuration: %w", err) - } - - bc.Logger().Infof("building image fileystem in %s", bc.o.WorkDir) - - apk, err := chainguardAPK.NewWithOptions(bc.fs, bc.o) - if err != nil { - return err - } - - if err := apk.Initialize(ctx, bc.ic); err != nil { - return fmt.Errorf("initializing apk: %w", err) - } - - if err := apk.Install(ctx); err != nil { + if err := bc.apk.FixateWorld(ctx, &bc.o.SourceDateEpoch); err != nil { return fmt.Errorf("installing apk packages: %w", err) } @@ -235,7 +210,12 @@ func (bc *Context) buildImage(ctx context.Context) error { } // add busybox symlinks - if err := installBusyboxLinks(bc.fs, &bc.o); err != nil { + installed, err := bc.apk.GetInstalled() + if err != nil { + return fmt.Errorf("getting installed packages: %w", err) + } + + if err := installBusyboxLinks(bc.fs, installed); err != nil { return err } @@ -276,22 +256,7 @@ func (bc *Context) WriteIndex(idx oci.SignedImageIndex) (string, int64, error) { } func (bc *Context) BuildPackageList(ctx context.Context) (toInstall []*repository.RepositoryPackage, conflicts []string, err error) { - bc.Logger().Infof("doing pre-flight checks") - if err := bc.ic.Validate(); err != nil { - return toInstall, conflicts, fmt.Errorf("failed to validate configuration: %w", err) - } - - bc.Logger().Infof("building apk info in %s", bc.o.WorkDir) - - apk, err := chainguardAPK.NewWithOptions(bc.fs, bc.o) - if err != nil { - return toInstall, conflicts, fmt.Errorf("initializing apk: %w", err) - } - if err := apk.Initialize(ctx, bc.ic); err != nil { - return toInstall, conflicts, fmt.Errorf("initializing apk: %w", err) - } - - if toInstall, conflicts, err = apk.ResolvePackages(ctx); err != nil { + if toInstall, conflicts, err = bc.apk.ResolveWorld(ctx); err != nil { return toInstall, conflicts, fmt.Errorf("resolving apk packages: %w", err) } bc.Logger().Infof("finished gathering apk info in %s", bc.o.WorkDir) diff --git a/pkg/build/busybox.go b/pkg/build/busybox.go index b03490929..8a27a6459 100644 --- a/pkg/build/busybox.go +++ b/pkg/build/busybox.go @@ -31,10 +31,8 @@ import ( "regexp" "strings" + "github.com/chainguard-dev/go-apk/pkg/apk" apkfs "github.com/chainguard-dev/go-apk/pkg/fs" - - chainguardAPK "chainguard.dev/apko/pkg/apk" - "chainguard.dev/apko/pkg/options" ) const ( @@ -53,7 +51,7 @@ var busyboxLinks map[string][]string // note that it changes based on version of busybox, // so this should be updated to match busybox version. -func installBusyboxLinks(fsys apkfs.FullFS, o *options.Options) error { +func installBusyboxLinks(fsys apkfs.FullFS, installed []*apk.InstalledPackage) error { // does busybox exist? if not, do not bother with symlinks if _, err := fsys.Stat(busybox); err != nil { if !errors.Is(err, os.ErrNotExist) { @@ -61,15 +59,6 @@ func installBusyboxLinks(fsys apkfs.FullFS, o *options.Options) error { } return nil } - // get the busybox version - apk, err := chainguardAPK.NewWithOptions(fsys, *o) - if err != nil { - return err - } - installed, err := apk.GetInstalled() - if err != nil { - return err - } var ( installedVersion string pkgName string diff --git a/pkg/build/busybox_test.go b/pkg/build/busybox_test.go index dfa5dfd89..028286a10 100644 --- a/pkg/build/busybox_test.go +++ b/pkg/build/busybox_test.go @@ -4,12 +4,10 @@ import ( "strings" "testing" - impl "github.com/chainguard-dev/go-apk/pkg/apk" + "github.com/chainguard-dev/go-apk/pkg/apk" apkfs "github.com/chainguard-dev/go-apk/pkg/fs" "github.com/stretchr/testify/require" "gitlab.alpinelinux.org/alpine/go/repository" - - "chainguard.dev/apko/pkg/options" ) // Copyright 2023 Chainguard, Inc. @@ -44,17 +42,20 @@ func TestInstallBusyboxSymlinks(t *testing.T) { require.NoError(t, err) err = fsys.MkdirAll("/lib/apk/db", 0755) require.NoError(t, err) - pkgLines := impl.PackageToIndex(pkg) + pkgLines := apk.PackageToIndex(pkg) err = fsys.WriteFile("/lib/apk/db/installed", []byte(strings.Join(pkgLines, "\n")+"\n\n"), 0755) require.NoError(t, err) } + installed := []*apk.InstalledPackage{{ + Package: *pkg, + }} t.Run("with busybox-paths manifest", func(t *testing.T) { var err error fsys := apkfs.NewMemFS() buildBusybox(fsys, t) err = fsys.WriteFile("/etc/busybox-paths.d/busybox", []byte(strings.Join(fakeLinks, "\n")), 0755) require.NoError(t, err) - err = installBusyboxLinks(fsys, &options.Options{}) + err = installBusyboxLinks(fsys, installed) require.NoError(t, err) for _, link := range fakeLinks { _, err := fsys.Lstat(link) @@ -72,7 +73,7 @@ func TestInstallBusyboxSymlinks(t *testing.T) { var err error fsys := apkfs.NewMemFS() buildBusybox(fsys, t) - err = installBusyboxLinks(fsys, &options.Options{}) + err = installBusyboxLinks(fsys, installed) require.NoError(t, err) for _, link := range fakeLinks { _, err := fsys.Lstat(link)