Coder Social home page Coder Social logo

go-apk's Introduction

go-apk

Warning

This library is no longer maintained as a separate standalone package. Instead, it's been re-absorbed into apko.

A native go implementation of the functionality of the Alpine Package Keeper client utility apk.

Also includes supporting utilities for working with filesystems, including:

  • an interface for a fully functional fs.FS with read-write, chmod/chown, devices, and symlinks capabilities
  • an implementation of that FS in memory, i.e. a memfs
  • an implementation of that FS on top of a directory, which uses the memfs for features the underlying disk does not support
  • tarball features

Documentation is available at https://pkg.go.dev/github.com/chainguard-dev/go-apk.

Usage

import (
    "github.com/chainguard-dev/go-apk/pkg/apk"
    "github.com/chainguard-dev/go-apk/pkg/fs"
)

fsys := fs.NewMemFS()
a, err := apk.New(
		apk.WithFS(fsys),
		apk.WithArch("aarch64"),
	)
a.InitDB("3.16", "3.17") // ensure basic structures and set up the database, fetches keys for those OS versions
a.InitKeyring([]string{"/etc/apk/keyfiles/key1"}, nil)
a.SetRepositories([]string{"https://dl-cdn.alpinelinux.org/alpine/v3.14/main"})
a.SetWorld([]string{"curl", "vim"})    // set the packages in /etc/apk/world
a.FixateWorld()              // install packages based on the contents of /etc/apk/world

Wherever possible the methods on apk that manipulate data are available standalone, so you can work with them outside of a given FullFS.

Components

Filesystems

The native go fs.FS interface is a read-only filesystem with no support for full capabilities like read-write, let alone symlinks, hardlinks, chown/chmod, devices, etc.

That makes it useful for reading, but not very useful for cases where you need to lay down data, like installing packages from a package manager.

github.com/chainguard-dev/go-apk/pkg/fs provides a FullFS interface that extends the fs.FS interface with full read-write, chmod/chown, devices, and symlinks capabilities. You can do pretty much anything that you can do with a normal filesystem.

It is fully compliant with fs.FS, so you can use it anywhere an fs.FS is required.

It also provides two implementations of that interface:

  • memfs is an in-memory implementation of FullFS. It is fully functional, but remember that it uses memory, so loading very large files into it will hit limits.
  • rwosfs is an on-disk implementation of FullFS. It is fully functional, including capabilities that may not exist on the underlying filesystem, like symlinks, devices, chown/chmod and case-sensitivity. The metadata for every file on disk also is in-memory, enabling those additional capabilities. Contents are not stored in memory.

Tarball

github.com/chainguard-dev/go-apk/pkg/tarball provides a utility to write an fs.FS to a tarball. It is implemented on a tarball.Context, which lets you provide overrides for timestamps, UID/GID, and other features.

apk

github.com/chainguard-dev/go-apk/pkg/apk is the heart of this library. It provides a native go implementation of the functionality of the Alpine Package Keeper with regards to reading repositories, installing packages, and managing a local install.

Caching

This package provides an option to cache apk packages locally. This can provide dramatic speedups when installing packages, especially when the package is large or the network is slow and you already have a copy.

It is enabled only when the WithCache() option is provided to the New() function.

When the cache is enabled, any requested apk files are checked in the cache first, and only downloaded in the case of a cache miss. The now-cached apk can be used in subsequent calls. To ignore the cache, simple do not pass WithCache() to New().

See CACHE.md for more details on the cache structure.

go-apk's People

Contributors

cpanato avatar deitch avatar dependabot[bot] avatar djcass44 avatar epsilon-phase avatar imjasonh avatar joemiller avatar jonjohnsonjr avatar kaniini avatar luhring avatar mattmoor avatar sfc-gh-mhazy avatar sfc-gh-ptabor avatar thesayyn avatar vaikas avatar xnox avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

go-apk's Issues

Providing packages in world with constraints is not respected in existing list

Consider the following list of packages:

packages:
- git

git depends on libcurl, which depends on libcrypto, which depends on openssl-config. So the final graph should have openssl-config in it, at the highest version. Let's say that we have openssl-config at the highest version of 3.2.

This works fine.

Now consider this list:

packages:
- git
- openssl-config-special

where openssl-config-special provides openssl-config=3.1.0-r0, i.e. below the highest available, which, as described above, is 3.2. Because we explicitly provide openssl-config-special, we should take that one to satisfy git's openssl-config dependency, and not the regular one, which would be at 3.2. This, too, works.

Now provide our special version with a constraint.

packages:
- git
- openssl-config-special=3.1.0

Now, it fails. It will include openssl-config-special=3.1.0 in the final graph, but it also will include openssl-config=3.2.0.

The reason for this is because the map that contains the list of "existing dependencies" did not separate name from version before tracking, only after.

This was fixed in #55 , where we not only added the parent, but to do so, we ensured that the tracking list is by name.

This issue is mainly as a tracking issue, so we can know it in the past, as well as for us to add a test.

concurrent map read/write in memfs

Saw this in tf-apko's presubmit testing. It was just once, but also without -race so perhaps that'll flush it out quicker because I've never seen this before.

cc @deitch

Full panic trace below:

fatal error: concurrent map read and map write

goroutine 3647 [running]:
github.com/chainguard-dev/go-apk/pkg/fs.(*memFS).OpenFile(0xc000813560, {0xc000f60150, 0x22}, 0x242, 0x1a4)
	/home/runner/go/pkg/mod/github.com/chainguard-dev/[email protected]/pkg/fs/memfs.go:215 +0x118
github.com/chainguard-dev/go-apk/pkg/fs.(*memFS).WriteFile(0xc0016de0f0?, {0xc000f60150?, 0xc00056f180?}, {0x0, 0x0, 0x0}, 0x529ab1?)
	/home/runner/go/pkg/mod/github.com/chainguard-dev/[email protected]/pkg/fs/memfs.go:259 +0x6d
github.com/chainguard-dev/go-apk/pkg/fs.(*dirFS).WriteFile(0xc0009182d0, {0xc000f60150, 0x22}, {0xc00056f180, 0x320, 0x380}, 0x0?)
	/home/runner/go/pkg/mod/github.com/chainguard-dev/[email protected]/pkg/fs/rwosfs.go:422 +0x117
github.com/chainguard-dev/go-apk/pkg/apk.(*APK).InitKeyring.func1()
	/home/runner/go/pkg/mod/github.com/chainguard-dev/[email protected]/pkg/apk/implementation.go:381 +0x51f
golang.org/x/sync/errgroup.(*Group).Go.func1()
	/home/runner/go/pkg/mod/golang.org/x/[email protected]/errgroup/errgroup.go:75 +0x64
created by golang.org/x/sync/errgroup.(*Group).Go
	/home/runner/go/pkg/mod/golang.org/x/[email protected]/errgroup/errgroup.go:72 +0xa5

goroutine 1 [chan receive]:
testing.(*T).Run(0xc000482680, {0x1702e7e?, 0x56c745?}, 0x1761898)
	/opt/hostedtoolcache/go/1.20.3/x64/src/testing/testing.go:1630 +0x405
testing.runTests.func1(0x23434a0?)
	/opt/hostedtoolcache/go/1.20.3/x64/src/testing/testing.go:2036 +0x45
testing.tRunner(0xc000482680, 0xc0002d1c80)
	/opt/hostedtoolcache/go/1.20.3/x64/src/testing/testing.go:1576 +0x10b
testing.runTests(0xc00018c0a0?, {0x23278a0, 0x6, 0x6}, {0x80?, 0x17fb3841dd298?, 0x2342a00?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/testing/testing.go:2034 +0x489
testing.(*M).Run(0xc00018c0a0)
	/opt/hostedtoolcache/go/1.20.3/x64/src/testing/testing.go:1906 +0x63a
main.main()
	_testmain.go:88 +0x1c5

goroutine 2754 [semacquire]:
sync.runtime_Semacquire(0xc0007d46e0?)
	/opt/hostedtoolcache/go/1.20.3/x64/src/runtime/sema.go:62 +0x27
sync.(*WaitGroup).Wait(0xc00053c2c0?)
	/opt/hostedtoolcache/go/1.20.3/x64/src/sync/waitgroup.go:116 +0x4b
github.com/hashicorp/terraform-exec/tfexec.(*Terraform).runTerraformCmd(0xc002ae2340, {0x19112a0, 0xc0000460f8}, 0xc00053c2c0)
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/tfexec/cmd_linux.go:75 +0x4d5
github.com/hashicorp/terraform-exec/tfexec.(*Terraform).Apply(0x1911310?, {0x19112a0, 0xc0000460f8}, {0xc00041e200?, 0x16fe663?, 0x23?})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/tfexec/apply.go:100 +0x59
github.com/hashicorp/terraform-plugin-testing/internal/plugintest.(*WorkingDir).Apply(0xc0005b1270, {0x1911310, 0xc0000263f0})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/internal/plugintest/working_dir.go:244 +0x1e5
github.com/hashicorp/terraform-plugin-testing/helper/resource.testStepNewConfig.func5()
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/helper/resource/testing_new_config.go:89 +0x25
github.com/hashicorp/terraform-plugin-testing/helper/resource.runProviderCommand({0x1911310, 0xc0000263f0}, {0x191cd60, 0xc000808000}, 0xc00053a018, 0xc0005b1270, 0xc00053a600)
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/helper/resource/plugin.go:438 +0x27eb
github.com/hashicorp/terraform-plugin-testing/helper/resource.testStepNewConfig({_, _}, {_, _}, {0x0, 0xc0003ae7c0, 0x0, 0x0, 0xc0008b2fc0, 0x0, ...}, ...)
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/helper/resource/testing_new_config.go:88 +0x745
github.com/hashicorp/terraform-plugin-testing/helper/resource.runNewTest({0x1911310, 0xc0000263f0}, {0x191cd60, 0xc000808000}, {0x0, 0xc0003ae7c0, 0x0, 0x0, 0xc0008b2fc0, 0x0, ...}, ...)
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/helper/resource/testing_new.go:295 +0x1370
github.com/hashicorp/terraform-plugin-testing/helper/resource.Test({0x191cd60, 0xc000808000}, {0x0, 0xc0003ae7c0, 0x0, 0x0, 0xc0008b2fc0, 0x0, 0x0, 0x0, ...})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/helper/resource/testing.go:827 +0x64f
github.com/chainguard-dev/terraform-provider-apko/internal/provider.TestAccResourceApkoBuild_ProviderOpts(0xc000808000)
	/home/runner/work/terraform-provider-apko/terraform-provider-apko/internal/provider/resource_build_test.go:120 +0x7cb
testing.tRunner(0xc000808000, 0x1761898)
	/opt/hostedtoolcache/go/1.20.3/x64/src/testing/testing.go:1576 +0x10b
created by testing.(*T).Run
	/opt/hostedtoolcache/go/1.20.3/x64/src/testing/testing.go:1629 +0x3ea

goroutine 3602 [select]:
github.com/hashicorp/go-plugin.(*gRPCBrokerServer).Recv(0xc0007d4230?)
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/grpc_broker.go:124 +0x67
github.com/hashicorp/go-plugin.(*GRPCBroker).Run(0xc0007d43c0)
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/grpc_broker.go:414 +0x44
created by github.com/hashicorp/go-plugin.(*GRPCServer).Init
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/grpc_server.go:88 +0x46a

goroutine 3608 [IO wait]:
internal/poll.runtime_pollWait(0x7fb37cfc1af0, 0x72)
	/opt/hostedtoolcache/go/1.20.3/x64/src/runtime/netpoll.go:306 +0x89
internal/poll.(*pollDesc).wait(0xc00133e660?, 0xc0012ee000?, 0x1)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:84 +0x32
internal/poll.(*pollDesc).waitRead(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc00133e660, {0xc0012ee000, 0x1000, 0x1000})
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_unix.go:167 +0x299
os.(*File).read(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/os/file_posix.go:31
os.(*File).Read(0xc00048e448, {0xc0012ee000?, 0xc0006add40?, 0x418410?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/os/file.go:118 +0x5e
bufio.(*Reader).fill(0xc0006adf08)
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:106 +0xff
bufio.(*Reader).ReadSlice(0xc0006adf08, 0x60?)
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:372 +0x2f
bufio.(*Reader).collectFragments(0x38?, 0x8d?)
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:447 +0x74
bufio.(*Reader).ReadBytes(0x19112a0?, 0xf8?)
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:474 +0x1d
github.com/hashicorp/terraform-exec/tfexec.writeOutput({0x19112a0?, 0xc0000460f8}, {0x190cca0?, 0xc00048e448}, {0x1904aa0, 0xc000856840})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/tfexec/cmd.go:260 +0x294
github.com/hashicorp/terraform-exec/tfexec.(*Terraform).runTerraformCmd.func2()
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/tfexec/cmd_linux.go:70 +0x79
created by github.com/hashicorp/terraform-exec/tfexec.(*Terraform).runTerraformCmd
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/tfexec/cmd_linux.go:68 +0x4cb

goroutine 3053 [IO wait]:
internal/poll.runtime_pollWait(0x7fb37ce8cb28, 0x72)
	/opt/hostedtoolcache/go/1.20.3/x64/src/runtime/netpoll.go:306 +0x89
internal/poll.(*pollDesc).wait(0xc000d27780?, 0xc0005dd000?, 0x0)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:84 +0x32
internal/poll.(*pollDesc).waitRead(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc000d27780, {0xc0005dd000, 0x1000, 0x1000})
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_unix.go:167 +0x299
net.(*netFD).Read(0xc000d27780, {0xc0005dd000?, 0x2?, 0x2329c00?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/fd_posix.go:55 +0x29
net.(*conn).Read(0xc0022d0088, {0xc0005dd000?, 0x0?, 0x0?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/net.go:183 +0x45
net/http.(*persistConn).Read(0xc00015c120, {0xc0005dd000?, 0x44d4e0?, 0xc0006a9ec8?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:1943 +0x4e
bufio.(*Reader).fill(0xc000776780)
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:106 +0xff
bufio.(*Reader).Peek(0xc000776780, 0x1)
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:144 +0x5d
net/http.(*persistConn).readLoop(0xc00015c120)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:2107 +0x1ac
created by net/http.(*Transport).dialConn
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:1765 +0x16ea

goroutine 3636 [select]:
net/http.(*http2ClientConn).RoundTrip(0xc00129cf00, 0xc000497400)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/h2_bundle.go:8231 +0x491
net/http.(*http2Transport).RoundTripOpt(0xc0002d0360, 0xc000497400, {0xc0?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/h2_bundle.go:7523 +0x1c5
net/http.(*http2Transport).RoundTrip(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/h2_bundle.go:7475
net/http.http2noDialH2RoundTripper.RoundTrip({0x2329e80?}, 0xc000497400?)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/h2_bundle.go:10060 +0x1b
net/http.(*Transport).roundTrip(0x2329e80, 0xc000497400)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:548 +0x3ca
net/http.(*Transport).RoundTrip(0x100?, 0x1904660?)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/roundtrip.go:17 +0x19
net/http.send(0xc000497400, {0x1904660, 0x2329e80}, {0x8?, 0x169b2e0?, 0x0?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/client.go:252 +0x5f7
net/http.(*Client).send(0xc00048c870, 0xc000497400, {0xc00088629a?, 0x1b?, 0x0?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/client.go:176 +0x9b
net/http.(*Client).do(0xc00048c870, 0xc000497400)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/client.go:716 +0x8fb
net/http.(*Client).Do(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/client.go:582
net/http.(*Client).Get(0xc0005557a0?, {0xc000886280?, 0xc0002b9028?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/client.go:480 +0x6a
github.com/chainguard-dev/go-apk/pkg/apk.GetRepositoryIndexes({0xc000b881a0, 0x2, 0xc000f0b4b8?}, 0xc0002b9218, {0xc002a094f0, 0x7}, {0xc0002b91b8, 0x2, 0x14611e0?})
	/home/runner/go/pkg/mod/github.com/chainguard-dev/[email protected]/pkg/apk/index.go:97 +0x3e5
github.com/chainguard-dev/go-apk/pkg/apk.(*APK).getRepositoryIndexes(0xc000000ba0, 0x0)
	/home/runner/go/pkg/mod/github.com/chainguard-dev/[email protected]/pkg/apk/repo.go:142 +0x6af
github.com/chainguard-dev/go-apk/pkg/apk.(*APK).ResolveWorld(0xc000000ba0)
	/home/runner/go/pkg/mod/github.com/chainguard-dev/[email protected]/pkg/apk/implementation.go:399 +0x53
github.com/chainguard-dev/go-apk/pkg/apk.(*APK).FixateWorld(0xc000000ba0, 0xc001916870?)
	/home/runner/go/pkg/mod/github.com/chainguard-dev/[email protected]/pkg/apk/implementation.go:430 +0xc5
chainguard.dev/apko/pkg/apk.(*APK).Install(...)
	/home/runner/go/pkg/mod/chainguard.dev/[email protected]/pkg/apk/apk.go:116
chainguard.dev/apko/pkg/build.(*defaultBuildImplementation).InstallPackages(0xc001916870?, {0x191d140?, 0xc000918570?}, 0xc000813cb0?, 0x7fb3ad55a108?)
	/home/runner/go/pkg/mod/chainguard.dev/[email protected]/pkg/build/build_implementation.go:216 +0xb5
chainguard.dev/apko/pkg/build.buildImage({0x191d140, 0xc000918570}, {0x191cea0, 0xc000813830}, 0xc000ddd380, 0x4?, 0x5?)
	/home/runner/go/pkg/mod/chainguard.dev/[email protected]/pkg/build/build_implementation.go:271 +0x232
chainguard.dev/apko/pkg/build.(*Context).BuildImage(0xc000ddd180)
	/home/runner/go/pkg/mod/chainguard.dev/[email protected]/pkg/build/build.go:88 +0x6d
chainguard.dev/apko/pkg/build.(*Context).BuildLayer(0xc000ddd180?)
	/home/runner/go/pkg/mod/chainguard.dev/[email protected]/pkg/build/build.go:122 +0x28
github.com/chainguard-dev/terraform-provider-apko/internal/provider.doBuild.func1()
	/home/runner/work/terraform-provider-apko/terraform-provider-apko/internal/provider/build.go:97 +0x12a
golang.org/x/sync/errgroup.(*Group).Go.func1()
	/home/runner/go/pkg/mod/golang.org/x/[email protected]/errgroup/errgroup.go:75 +0x64
created by golang.org/x/sync/errgroup.(*Group).Go
	/home/runner/go/pkg/mod/golang.org/x/[email protected]/errgroup/errgroup.go:72 +0xa5

goroutine 3605 [chan receive]:
github.com/hashicorp/terraform-plugin-testing/helper/resource.runProviderCommand.func6(0xc00041e1c0?)
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/helper/resource/plugin.go:414 +0x25
created by github.com/hashicorp/terraform-plugin-testing/helper/resource.runProviderCommand
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/helper/resource/plugin.go:413 +0x260a

goroutine 3621 [IO wait]:
internal/poll.runtime_pollWait(0x7fb37ce8cfd8, 0x72)
	/opt/hostedtoolcache/go/1.20.3/x64/src/runtime/netpoll.go:306 +0x89
internal/poll.(*pollDesc).wait(0xc000d26280?, 0xc000380000?, 0x0)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:84 +0x32
internal/poll.(*pollDesc).waitRead(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc000d26280, {0xc000380000, 0x8000, 0x8000})
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_unix.go:167 +0x299
net.(*netFD).Read(0xc000d26280, {0xc000380000?, 0x1060100000000?, 0x8?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/fd_posix.go:55 +0x29
net.(*conn).Read(0xc0022d0008, {0xc000380000?, 0x18?, 0x23434a0?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/net.go:183 +0x45
bufio.(*Reader).Read(0xc000e8a180, {0xc000108d60, 0x9, 0x0?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:237 +0x1bb
io.ReadAtLeast({0x18fed40, 0xc000e8a180}, {0xc000108d60, 0x9, 0x9}, 0x9)
	/opt/hostedtoolcache/go/1.20.3/x64/src/io/io.go:332 +0x9a
io.ReadFull(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/io/io.go:351
golang.org/x/net/http2.readFrameHeader({0xc000108d60?, 0x9?, 0xc001112420?}, {0x18fed40?, 0xc000e8a180?})
	/home/runner/go/pkg/mod/golang.org/x/[email protected]/http2/frame.go:237 +0x6e
golang.org/x/net/http2.(*Framer).ReadFrame(0xc000108d20)
	/home/runner/go/pkg/mod/golang.org/x/[email protected]/http2/frame.go:498 +0x95
google.golang.org/grpc/internal/transport.(*http2Server).HandleStreams(0xc00047c820, 0x0?, 0x0?)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/http2_server.go:637 +0x174
google.golang.org/grpc.(*Server).serveStreams(0xc000545e00, {0x1918a40?, 0xc00047c820})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:950 +0x189
google.golang.org/grpc.(*Server).handleRawConn.func1()
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:892 +0x46
created by google.golang.org/grpc.(*Server).handleRawConn
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:891 +0x185

goroutine 3616 [select]:
github.com/hashicorp/go-plugin.(*gRPCBrokerServer).StartStream.func1()
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/grpc_broker.go:73 +0x105
created by github.com/hashicorp/go-plugin.(*gRPCBrokerServer).StartStream
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/grpc_broker.go:71 +0x131

goroutine 3073 [select]:
net/http.(*persistConn).writeLoop(0xc001218120)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:2410 +0xf2
created by net/http.(*Transport).dialConn
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:1766 +0x173d

goroutine 3615 [select]:
google.golang.org/grpc/internal/transport.(*recvBufferReader).read(0xc0007d4a50, {0xc000fcc298, 0x5, 0x5})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/transport.go:183 +0x90
google.golang.org/grpc/internal/transport.(*recvBufferReader).Read(0xc0007d4a50, {0xc000fcc298?, 0xc001112318?, 0xc00075f668?})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/transport.go:177 +0x178
google.golang.org/grpc/internal/transport.(*transportReader).Read(0xc0006a3590, {0xc000fcc298?, 0xc00075f6e0?, 0x10d3a67?})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/transport.go:514 +0x32
io.ReadAtLeast({0x1903b00, 0xc0006a3590}, {0xc000fcc298, 0x5, 0x5}, 0x5)
	/opt/hostedtoolcache/go/1.20.3/x64/src/io/io.go:332 +0x9a
io.ReadFull(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/io/io.go:351
google.golang.org/grpc/internal/transport.(*Stream).Read(0xc000448a20, {0xc000fcc298, 0x5, 0x5})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/transport.go:498 +0xac
google.golang.org/grpc.(*parser).recvMsg(0xc000fcc288, 0x10000000)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/rpc_util.go:597 +0x47
google.golang.org/grpc.recvAndDecompress(0x0?, 0xc000448a20, {0x0, 0x0}, 0x10000000, 0x0, {0x0, 0x0})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/rpc_util.go:729 +0x66
google.golang.org/grpc.recv(0x1575b60?, {0x7fb37cf547d8, 0x2373278}, 0x10b869d?, {0x0?, 0x0?}, {0x164a740, 0xc0007d4aa0}, 0xc0007d4830?, 0x0, ...)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/rpc_util.go:795 +0x6e
google.golang.org/grpc.(*serverStream).RecvMsg(0xc00074a000, {0x164a740?, 0xc0007d4aa0})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/stream.go:1699 +0x178
github.com/hashicorp/go-plugin/internal/plugin.(*gRPCBrokerStartStreamServer).Recv(0xc000d50340)
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/internal/plugin/grpc_broker.pb.go:167 +0x4c
github.com/hashicorp/go-plugin.(*gRPCBrokerServer).StartStream(0xc0006a2c60, {0x19179f0, 0xc000d50340})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/grpc_broker.go:87 +0x150
github.com/hashicorp/go-plugin/internal/plugin._GRPCBroker_StartStream_Handler({0x1552de0?, 0xc0006a2c60}, {0x1914810?, 0xc00074a000})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/internal/plugin/grpc_broker.pb.go:148 +0x9f
google.golang.org/grpc.(*Server).processStreamingRPC(0xc000545e00, {0x1918a40, 0xc000832340}, 0xc000448a20, 0xc0006a2cf0, 0x231dc20, 0x0)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:1639 +0x1384
google.golang.org/grpc.(*Server).handleStream(0xc000545e00, {0x1918a40, 0xc000832340}, 0xc000448a20, 0x0)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:1726 +0x9f0
google.golang.org/grpc.(*Server).serveStreams.func1.2()
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:966 +0x98
created by google.golang.org/grpc.(*Server).serveStreams.func1
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:964 +0x28a

goroutine 3580 [select]:
google.golang.org/grpc/internal/transport.(*recvBufferReader).read(0xc000fc68c0, {0xc000fcc838, 0x5, 0x5})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/transport.go:183 +0x90
google.golang.org/grpc/internal/transport.(*recvBufferReader).Read(0xc000fc68c0, {0xc000fcc838?, 0xc000f0a990?, 0xc00049e668?})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/transport.go:177 +0x178
google.golang.org/grpc/internal/transport.(*transportReader).Read(0xc0003fb2f0, {0xc000fcc838?, 0xc00049e6e0?, 0x10d3a67?})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/transport.go:514 +0x32
io.ReadAtLeast({0x1903b00, 0xc0003fb2f0}, {0xc000fcc838, 0x5, 0x5}, 0x5)
	/opt/hostedtoolcache/go/1.20.3/x64/src/io/io.go:332 +0x9a
io.ReadFull(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/io/io.go:351
google.golang.org/grpc/internal/transport.(*Stream).Read(0xc00015c360, {0xc000fcc838, 0x5, 0x5})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/transport.go:498 +0xac
google.golang.org/grpc.(*parser).recvMsg(0xc000fcc828, 0x10000000)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/rpc_util.go:597 +0x47
google.golang.org/grpc.recvAndDecompress(0xf46319?, 0xc00015c360, {0x0, 0x0}, 0x10000000, 0x0, {0x0, 0x0})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/rpc_util.go:729 +0x66
google.golang.org/grpc.recv(0xc0006fe900?, {0x7fb37cf547d8, 0x2373278}, 0x1642640?, {0x0?, 0x0?}, {0x164a740, 0xc0007d5400}, 0xc0006fe900?, 0x0, ...)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/rpc_util.go:795 +0x6e
google.golang.org/grpc.(*serverStream).RecvMsg(0xc00074a0f0, {0x164a740?, 0xc0007d5400})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/stream.go:1699 +0x178
github.com/hashicorp/go-plugin/internal/plugin.(*gRPCBrokerStartStreamServer).Recv(0xc0012162c0)
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/internal/plugin/grpc_broker.pb.go:167 +0x4c
github.com/hashicorp/go-plugin.(*gRPCBrokerServer).StartStream(0xc0006a2c60, {0x19179f0, 0xc0012162c0})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/grpc_broker.go:87 +0x150
github.com/hashicorp/go-plugin/internal/plugin._GRPCBroker_StartStream_Handler({0x1552de0?, 0xc0006a2c60}, {0x1914810?, 0xc00074a0f0})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/internal/plugin/grpc_broker.pb.go:148 +0x9f
google.golang.org/grpc.(*Server).processStreamingRPC(0xc000545e00, {0x1918a40, 0xc00047c820}, 0xc00015c360, 0xc0006a2cf0, 0x231dc20, 0x0)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:1639 +0x1384
google.golang.org/grpc.(*Server).handleStream(0xc000545e00, {0x1918a40, 0xc00047c820}, 0xc00015c360, 0x0)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:1726 +0x9f0
google.golang.org/grpc.(*Server).serveStreams.func1.2()
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:966 +0x98
created by google.golang.org/grpc.(*Server).serveStreams.func1
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:964 +0x28a
internal/poll.(*pollDesc).wait(0xc000f0dd80?, 0xc00283e000?, 0x0)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:84 +0x32
internal/poll.(*pollDesc).waitRead(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc000f0dd80, {0xc00283e000, 0xa000, 0xa000})
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_unix.go:167 +0x299
net.(*netFD).Read(0xc000f0dd80, {0xc00283e000?, 0xc00283e060?, 0x33a?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/fd_posix.go:55 +0x29
net.(*conn).Read(0xc00048eea0, {0xc00283e000?, 0x444351?, 0xc00128f330?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/net.go:183 +0x45
crypto/tls.(*atLeastReader).Read(0xc000b7e228, {0xc00283e000?, 0xc000b7e228?, 0x0?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/crypto/tls/conn.go:788 +0x3d
bytes.(*Buffer).ReadFrom(0xc00128f410, {0x18ff040, 0xc000b7e228})
	/opt/hostedtoolcache/go/1.20.3/x64/src/bytes/buffer.go:202 +0x98
crypto/tls.(*Conn).readFromUntil(0xc00128f180, {0x1904520?, 0xc00048eea0}, 0x9fa5?)
	/opt/hostedtoolcache/go/1.20.3/x64/src/crypto/tls/conn.go:810 +0xe5
crypto/tls.(*Conn).readRecordOrCCS(0xc00128f180, 0x0)
	/opt/hostedtoolcache/go/1.20.3/x64/src/crypto/tls/conn.go:617 +0x116
crypto/tls.(*Conn).readRecord(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/crypto/tls/conn.go:583
crypto/tls.(*Conn).Read(0xc00128f180, {0xc0014e2000, 0x1000, 0x8ecc80?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/crypto/tls/conn.go:1316 +0x16f
bufio.(*Reader).Read(0xc00133fa40, {0xc0007184a0, 0x9, 0x90a905?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:237 +0x1bb
io.ReadAtLeast({0x18fed40, 0xc00133fa40}, {0xc0007184a0, 0x9, 0x9}, 0x9)
	/opt/hostedtoolcache/go/1.20.3/x64/src/io/io.go:332 +0x9a
io.ReadFull(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/io/io.go:351
net/http.http2readFrameHeader({0xc0007184a0?, 0x9?, 0xc0006a3800?}, {0x18fed40?, 0xc00133fa40?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/h2_bundle.go:1567 +0x6e
net/http.(*http2Framer).ReadFrame(0xc000718460)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/h2_bundle.go:1831 +0x95
net/http.(*http2clientConnReadLoop).run(0xc0003c8f98)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/h2_bundle.go:9187 +0x12e
net/http.(*http2ClientConn).readLoop(0xc00129cf00)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/h2_bundle.go:9082 +0x6f
created by net/http.(*http2Transport).newClientConn
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/h2_bundle.go:7779 +0xc3c

goroutine 3620 [select]:
google.golang.org/grpc/internal/transport.(*http2Server).keepalive(0xc00047c820)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/http2_server.go:[115](https://github.com/chainguard-dev/terraform-provider-apko/actions/runs/4916989168/jobs/8781471552?pr=52#step:7:116)0 +0x233
created by google.golang.org/grpc/internal/transport.NewServerTransport
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/http2_server.go:339 +0x1af8

goroutine 3072 [IO wait]:
internal/poll.runtime_pollWait(0x7fb37cfc[119](https://github.com/chainguard-dev/terraform-provider-apko/actions/runs/4916989168/jobs/8781471552?pr=52#step:7:120)0, 0x72)
	/opt/hostedtoolcache/go/1.20.3/x64/src/runtime/netpoll.go:306 +0x89
internal/poll.(*pollDesc).wait(0xc00053e600?, 0xc000370000?, 0x0)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:84 +0x32
internal/poll.(*pollDesc).waitRead(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc00053e600, {0xc000370000, 0x1000, 0x1000})
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_unix.go:167 +0x299
net.(*netFD).Read(0xc00053e600, {0xc000370000?, 0x2?, 0x2329c00?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/fd_posix.go:55 +0x29
net.(*conn).Read(0xc00087c7d8, {0xc000370000?, 0x0?, 0x0?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/net.go:183 +0x45
net/http.(*persistConn).Read(0xc001218[120](https://github.com/chainguard-dev/terraform-provider-apko/actions/runs/4916989168/jobs/8781471552?pr=52#step:7:121), {0xc000370000?, 0x44d4e0?, 0xc0006a8ec8?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:1943 +0x4e
bufio.(*Reader).fill(0xc000da0cc0)
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:106 +0xff
bufio.(*Reader).Peek(0xc000da0cc0, 0x1)
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:144 +0x5d
net/http.(*persistConn).readLoop(0xc00[121](https://github.com/chainguard-dev/terraform-provider-apko/actions/runs/4916989168/jobs/8781471552?pr=52#step:7:122)8120)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:2107 +0x1ac
created by net/http.(*Transport).dialConn
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:1765 +0x16ea

goroutine 3054 [select]:
net/http.(*persistConn).writeLoop(0xc00015c120)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:2410 +0xf2
created by net/http.(*Transport).dialConn
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:1766 +0x173d

goroutine 3610 [select]:
github.com/hashicorp/terraform-exec/tfexec.writeOutput.func1()
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/tfexec/cmd.go:250 +0xa6
created by github.com/hashicorp/terraform-exec/tfexec.writeOutput
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/tfexec/cmd.go:249 +0x14a

goroutine 3619 [select]:
google.golang.org/grpc/internal/transport.(*controlBuffer).get(0xc000fc66e0, 0x1)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/controlbuf.go:417 +0x115
google.golang.org/grpc/internal/transport.(*loopyWriter).run(0xc0004d2850)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/controlbuf.go:549 +0x91
google.golang.org/grpc/internal/transport.NewServerTransport.func2()
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/http2_server.go:336 +0xce
created by google.golang.org/grpc/internal/transport.NewServerTransport
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/http2_server.go:333 +0x1ab3

goroutine 3577 [select]:
github.com/hashicorp/go-plugin.(*grpcStdioServer).StreamStdio(0xc000e557c0, 0x1587300?, {0x1916a80, 0xc000e55c50})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/grpc_stdio.go:61 +0x11d
github.com/hashicorp/go-plugin/internal/plugin._GRPCStdio_StreamStdio_Handler({0x14b4600?, 0xc000e557c0}, {0x1914810, 0xc0006040f0})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/internal/plugin/grpc_stdio.pb.go:185 +0xd0
google.golang.org/grpc.(*Server).processStreamingRPC(0xc000545e00, {0x1918a40, 0xc000832340}, 0xc000239e60, 0xc0006a2ea0, 0x231dc40, 0x0)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:1639 +0x[138](https://github.com/chainguard-dev/terraform-provider-apko/actions/runs/4916989168/jobs/8781471552?pr=52#step:7:139)4
google.golang.org/grpc.(*Server).handleStream(0xc000545e00, {0x1918a40, 0xc000832340}, 0xc000239e60, 0x0)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:[172](https://github.com/chainguard-dev/terraform-provider-apko/actions/runs/4916989168/jobs/8781471552?pr=52#step:7:173)6 +0x9f0
google.golang.org/grpc.(*Server).serveStreams.func1.2()
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:966 +0x98
created by google.golang.org/grpc.(*Server).serveStreams.func1
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:964 +0x28afatal error: concurrent map read and map write

goroutine 3647 [running]:
github.com/chainguard-dev/go-apk/pkg/fs.(*memFS).OpenFile(0xc000813560, {0xc000f60150, 0x22}, 0x242, 0x1a4)
	/home/runner/go/pkg/mod/github.com/chainguard-dev/[email protected]/pkg/fs/memfs.go:215 +0x118
github.com/chainguard-dev/go-apk/pkg/fs.(*memFS).WriteFile(0xc0016de0f0?, {0xc000f60150?, 0xc00056f180?}, {0x0, 0x0, 0x0}, 0x529ab1?)
	/home/runner/go/pkg/mod/github.com/chainguard-dev/[email protected]/pkg/fs/memfs.go:259 +0x6d
github.com/chainguard-dev/go-apk/pkg/fs.(*dirFS).WriteFile(0xc0009182d0, {0xc000f60150, 0x22}, {0xc00056f180, 0x320, 0x380}, 0x0?)
	/home/runner/go/pkg/mod/github.com/chainguard-dev/[email protected]/pkg/fs/rwosfs.go:422 +0x117
github.com/chainguard-dev/go-apk/pkg/apk.(*APK).InitKeyring.func1()
	/home/runner/go/pkg/mod/github.com/chainguard-dev/[email protected]/pkg/apk/implementation.go:381 +0x51f
golang.org/x/sync/errgroup.(*Group).Go.func1()
	/home/runner/go/pkg/mod/golang.org/x/[email protected]/errgroup/errgroup.go:75 +0x64
created by golang.org/x/sync/errgroup.(*Group).Go
	/home/runner/go/pkg/mod/golang.org/x/[email protected]/errgroup/errgroup.go:72 +0xa5

goroutine 1 [chan receive]:
testing.(*T).Run(0xc000482680, {0x1702e7e?, 0x56c745?}, 0x1761898)
	/opt/hostedtoolcache/go/1.20.3/x64/src/testing/testing.go:1630 +0x405
testing.runTests.func1(0x23434a0?)
	/opt/hostedtoolcache/go/1.20.3/x64/src/testing/testing.go:2036 +0x45
testing.tRunner(0xc000482680, 0xc0002d1c80)
	/opt/hostedtoolcache/go/1.20.3/x64/src/testing/testing.go:1576 +0x10b
testing.runTests(0xc00018c0a0?, {0x23278a0, 0x6, 0x6}, {0x80?, 0x17fb3841dd298?, 0x2342a00?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/testing/testing.go:2034 +0x489
testing.(*M).Run(0xc00018c0a0)
	/opt/hostedtoolcache/go/1.20.3/x64/src/testing/testing.go:1906 +0x63a
main.main()
	_testmain.go:88 +0x1c5

goroutine 2754 [semacquire]:
sync.runtime_Semacquire(0xc0007d46e0?)
	/opt/hostedtoolcache/go/1.20.3/x64/src/runtime/sema.go:62 +0x27
sync.(*WaitGroup).Wait(0xc00053c2c0?)
	/opt/hostedtoolcache/go/1.20.3/x64/src/sync/waitgroup.go:116 +0x4b
github.com/hashicorp/terraform-exec/tfexec.(*Terraform).runTerraformCmd(0xc002ae2340, {0x19112a0, 0xc0000460f8}, 0xc00053c2c0)
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/tfexec/cmd_linux.go:75 +0x4d5
github.com/hashicorp/terraform-exec/tfexec.(*Terraform).Apply(0x1911310?, {0x19112a0, 0xc0000460f8}, {0xc00041e200?, 0x16fe663?, 0x23?})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/tfexec/apply.go:100 +0x59
github.com/hashicorp/terraform-plugin-testing/internal/plugintest.(*WorkingDir).Apply(0xc0005b1270, {0x1911310, 0xc0000263f0})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/internal/plugintest/working_dir.go:244 +0x1e5
github.com/hashicorp/terraform-plugin-testing/helper/resource.testStepNewConfig.func5()
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/helper/resource/testing_new_config.go:89 +0x25
github.com/hashicorp/terraform-plugin-testing/helper/resource.runProviderCommand({0x1911310, 0xc0000263f0}, {0x191cd60, 0xc000808000}, 0xc00053a018, 0xc0005b1270, 0xc00053a600)
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/helper/resource/plugin.go:438 +0x27eb
github.com/hashicorp/terraform-plugin-testing/helper/resource.testStepNewConfig({_, _}, {_, _}, {0x0, 0xc0003ae7c0, 0x0, 0x0, 0xc0008b2fc0, 0x0, ...}, ...)
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/helper/resource/testing_new_config.go:88 +0x745
github.com/hashicorp/terraform-plugin-testing/helper/resource.runNewTest({0x1911310, 0xc0000263f0}, {0x191cd60, 0xc000808000}, {0x0, 0xc0003ae7c0, 0x0, 0x0, 0xc0008b2fc0, 0x0, ...}, ...)
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/helper/resource/testing_new.go:295 +0x1370
github.com/hashicorp/terraform-plugin-testing/helper/resource.Test({0x191cd60, 0xc000808000}, {0x0, 0xc0003ae7c0, 0x0, 0x0, 0xc0008b2fc0, 0x0, 0x0, 0x0, ...})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/helper/resource/testing.go:827 +0x64f
github.com/chainguard-dev/terraform-provider-apko/internal/provider.TestAccResourceApkoBuild_ProviderOpts(0xc000808000)
	/home/runner/work/terraform-provider-apko/terraform-provider-apko/internal/provider/resource_build_test.go:120 +0x7cb
testing.tRunner(0xc000808000, 0x1761898)
	/opt/hostedtoolcache/go/1.20.3/x64/src/testing/testing.go:1576 +0x10b
created by testing.(*T).Run
	/opt/hostedtoolcache/go/1.20.3/x64/src/testing/testing.go:1629 +0x3ea

goroutine 3602 [select]:
github.com/hashicorp/go-plugin.(*gRPCBrokerServer).Recv(0xc0007d4230?)
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/grpc_broker.go:124 +0x67
github.com/hashicorp/go-plugin.(*GRPCBroker).Run(0xc0007d43c0)
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/grpc_broker.go:414 +0x44
created by github.com/hashicorp/go-plugin.(*GRPCServer).Init
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/grpc_server.go:88 +0x46a

goroutine 3608 [IO wait]:
internal/poll.runtime_pollWait(0x7fb37cfc1af0, 0x72)
	/opt/hostedtoolcache/go/1.20.3/x64/src/runtime/netpoll.go:306 +0x89
internal/poll.(*pollDesc).wait(0xc00133e660?, 0xc0012ee000?, 0x1)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:84 +0x32
internal/poll.(*pollDesc).waitRead(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc00133e660, {0xc0012ee000, 0x1000, 0x1000})
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_unix.go:167 +0x299
os.(*File).read(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/os/file_posix.go:31
os.(*File).Read(0xc00048e448, {0xc0012ee000?, 0xc0006add40?, 0x418410?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/os/file.go:118 +0x5e
bufio.(*Reader).fill(0xc0006adf08)
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:106 +0xff
bufio.(*Reader).ReadSlice(0xc0006adf08, 0x60?)
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:372 +0x2f
bufio.(*Reader).collectFragments(0x38?, 0x8d?)
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:447 +0x74
bufio.(*Reader).ReadBytes(0x19112a0?, 0xf8?)
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:474 +0x1d
github.com/hashicorp/terraform-exec/tfexec.writeOutput({0x19112a0?, 0xc0000460f8}, {0x190cca0?, 0xc00048e448}, {0x1904aa0, 0xc000856840})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/tfexec/cmd.go:260 +0x294
github.com/hashicorp/terraform-exec/tfexec.(*Terraform).runTerraformCmd.func2()
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/tfexec/cmd_linux.go:70 +0x79
created by github.com/hashicorp/terraform-exec/tfexec.(*Terraform).runTerraformCmd
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/tfexec/cmd_linux.go:68 +0x4cb

goroutine 3053 [IO wait]:
internal/poll.runtime_pollWait(0x7fb37ce8cb28, 0x72)
	/opt/hostedtoolcache/go/1.20.3/x64/src/runtime/netpoll.go:306 +0x89
internal/poll.(*pollDesc).wait(0xc000d27780?, 0xc0005dd000?, 0x0)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:84 +0x32
internal/poll.(*pollDesc).waitRead(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc000d27780, {0xc0005dd000, 0x1000, 0x1000})
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_unix.go:167 +0x299
net.(*netFD).Read(0xc000d27780, {0xc0005dd000?, 0x2?, 0x2329c00?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/fd_posix.go:55 +0x29
net.(*conn).Read(0xc0022d0088, {0xc0005dd000?, 0x0?, 0x0?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/net.go:183 +0x45
net/http.(*persistConn).Read(0xc00015c120, {0xc0005dd000?, 0x44d4e0?, 0xc0006a9ec8?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:1943 +0x4e
bufio.(*Reader).fill(0xc000776780)
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:106 +0xff
bufio.(*Reader).Peek(0xc000776780, 0x1)
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:144 +0x5d
net/http.(*persistConn).readLoop(0xc00015c120)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:2107 +0x1ac
created by net/http.(*Transport).dialConn
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:1765 +0x16ea

goroutine 3636 [select]:
net/http.(*http2ClientConn).RoundTrip(0xc00129cf00, 0xc000497400)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/h2_bundle.go:8231 +0x491
net/http.(*http2Transport).RoundTripOpt(0xc0002d0360, 0xc000497400, {0xc0?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/h2_bundle.go:7523 +0x1c5
net/http.(*http2Transport).RoundTrip(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/h2_bundle.go:7475
net/http.http2noDialH2RoundTripper.RoundTrip({0x2329e80?}, 0xc000497400?)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/h2_bundle.go:10060 +0x1b
net/http.(*Transport).roundTrip(0x2329e80, 0xc000497400)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:548 +0x3ca
net/http.(*Transport).RoundTrip(0x100?, 0x1904660?)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/roundtrip.go:17 +0x19
net/http.send(0xc000497400, {0x1904660, 0x2329e80}, {0x8?, 0x169b2e0?, 0x0?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/client.go:252 +0x5f7
net/http.(*Client).send(0xc00048c870, 0xc000497400, {0xc00088629a?, 0x1b?, 0x0?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/client.go:176 +0x9b
net/http.(*Client).do(0xc00048c870, 0xc000497400)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/client.go:716 +0x8fb
net/http.(*Client).Do(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/client.go:582
net/http.(*Client).Get(0xc0005557a0?, {0xc000886280?, 0xc0002b9028?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/client.go:480 +0x6a
github.com/chainguard-dev/go-apk/pkg/apk.GetRepositoryIndexes({0xc000b881a0, 0x2, 0xc000f0b4b8?}, 0xc0002b9218, {0xc002a094f0, 0x7}, {0xc0002b91b8, 0x2, 0x14611e0?})
	/home/runner/go/pkg/mod/github.com/chainguard-dev/[email protected]/pkg/apk/index.go:97 +0x3e5
github.com/chainguard-dev/go-apk/pkg/apk.(*APK).getRepositoryIndexes(0xc000000ba0, 0x0)
	/home/runner/go/pkg/mod/github.com/chainguard-dev/[email protected]/pkg/apk/repo.go:142 +0x6af
github.com/chainguard-dev/go-apk/pkg/apk.(*APK).ResolveWorld(0xc000000ba0)
	/home/runner/go/pkg/mod/github.com/chainguard-dev/[email protected]/pkg/apk/implementation.go:399 +0x53
github.com/chainguard-dev/go-apk/pkg/apk.(*APK).FixateWorld(0xc000000ba0, 0xc001916870?)
	/home/runner/go/pkg/mod/github.com/chainguard-dev/[email protected]/pkg/apk/implementation.go:430 +0xc5
chainguard.dev/apko/pkg/apk.(*APK).Install(...)
	/home/runner/go/pkg/mod/chainguard.dev/[email protected]/pkg/apk/apk.go:116
chainguard.dev/apko/pkg/build.(*defaultBuildImplementation).InstallPackages(0xc001916870?, {0x191d140?, 0xc000918570?}, 0xc000813cb0?, 0x7fb3ad55a108?)
	/home/runner/go/pkg/mod/chainguard.dev/[email protected]/pkg/build/build_implementation.go:216 +0xb5
chainguard.dev/apko/pkg/build.buildImage({0x191d140, 0xc000918570}, {0x191cea0, 0xc000813830}, 0xc000ddd380, 0x4?, 0x5?)
	/home/runner/go/pkg/mod/chainguard.dev/[email protected]/pkg/build/build_implementation.go:271 +0x232
chainguard.dev/apko/pkg/build.(*Context).BuildImage(0xc000ddd180)
	/home/runner/go/pkg/mod/chainguard.dev/[email protected]/pkg/build/build.go:88 +0x6d
chainguard.dev/apko/pkg/build.(*Context).BuildLayer(0xc000ddd180?)
	/home/runner/go/pkg/mod/chainguard.dev/[email protected]/pkg/build/build.go:122 +0x28
github.com/chainguard-dev/terraform-provider-apko/internal/provider.doBuild.func1()
	/home/runner/work/terraform-provider-apko/terraform-provider-apko/internal/provider/build.go:97 +0x12a
golang.org/x/sync/errgroup.(*Group).Go.func1()
	/home/runner/go/pkg/mod/golang.org/x/[email protected]/errgroup/errgroup.go:75 +0x64
created by golang.org/x/sync/errgroup.(*Group).Go
	/home/runner/go/pkg/mod/golang.org/x/[email protected]/errgroup/errgroup.go:72 +0xa5

goroutine 3605 [chan receive]:
github.com/hashicorp/terraform-plugin-testing/helper/resource.runProviderCommand.func6(0xc00041e1c0?)
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/helper/resource/plugin.go:414 +0x25
created by github.com/hashicorp/terraform-plugin-testing/helper/resource.runProviderCommand
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/helper/resource/plugin.go:413 +0x260a

goroutine 3621 [IO wait]:
internal/poll.runtime_pollWait(0x7fb37ce8cfd8, 0x72)
	/opt/hostedtoolcache/go/1.20.3/x64/src/runtime/netpoll.go:306 +0x89
internal/poll.(*pollDesc).wait(0xc000d26280?, 0xc000380000?, 0x0)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:84 +0x32
internal/poll.(*pollDesc).waitRead(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc000d26280, {0xc000380000, 0x8000, 0x8000})
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_unix.go:167 +0x299
net.(*netFD).Read(0xc000d26280, {0xc000380000?, 0x1060100000000?, 0x8?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/fd_posix.go:55 +0x29
net.(*conn).Read(0xc0022d0008, {0xc000380000?, 0x18?, 0x23434a0?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/net.go:183 +0x45
bufio.(*Reader).Read(0xc000e8a180, {0xc000108d60, 0x9, 0x0?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:237 +0x1bb
io.ReadAtLeast({0x18fed40, 0xc000e8a180}, {0xc000108d60, 0x9, 0x9}, 0x9)
	/opt/hostedtoolcache/go/1.20.3/x64/src/io/io.go:332 +0x9a
io.ReadFull(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/io/io.go:351
golang.org/x/net/http2.readFrameHeader({0xc000108d60?, 0x9?, 0xc001112420?}, {0x18fed40?, 0xc000e8a180?})
	/home/runner/go/pkg/mod/golang.org/x/[email protected]/http2/frame.go:237 +0x6e
golang.org/x/net/http2.(*Framer).ReadFrame(0xc000108d20)
	/home/runner/go/pkg/mod/golang.org/x/[email protected]/http2/frame.go:498 +0x95
google.golang.org/grpc/internal/transport.(*http2Server).HandleStreams(0xc00047c820, 0x0?, 0x0?)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/http2_server.go:637 +0x174
google.golang.org/grpc.(*Server).serveStreams(0xc000545e00, {0x1918a40?, 0xc00047c820})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:950 +0x189
google.golang.org/grpc.(*Server).handleRawConn.func1()
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:892 +0x46
created by google.golang.org/grpc.(*Server).handleRawConn
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:891 +0x185

goroutine 3616 [select]:
github.com/hashicorp/go-plugin.(*gRPCBrokerServer).StartStream.func1()
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/grpc_broker.go:73 +0x105
created by github.com/hashicorp/go-plugin.(*gRPCBrokerServer).StartStream
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/grpc_broker.go:71 +0x131

goroutine 3073 [select]:
net/http.(*persistConn).writeLoop(0xc001218120)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:2410 +0xf2
created by net/http.(*Transport).dialConn
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:1766 +0x173d

goroutine 3615 [select]:
google.golang.org/grpc/internal/transport.(*recvBufferReader).read(0xc0007d4a50, {0xc000fcc298, 0x5, 0x5})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/transport.go:183 +0x90
google.golang.org/grpc/internal/transport.(*recvBufferReader).Read(0xc0007d4a50, {0xc000fcc298?, 0xc001112318?, 0xc00075f668?})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/transport.go:177 +0x178
google.golang.org/grpc/internal/transport.(*transportReader).Read(0xc0006a3590, {0xc000fcc298?, 0xc00075f6e0?, 0x10d3a67?})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/transport.go:514 +0x32
io.ReadAtLeast({0x1903b00, 0xc0006a3590}, {0xc000fcc298, 0x5, 0x5}, 0x5)
	/opt/hostedtoolcache/go/1.20.3/x64/src/io/io.go:332 +0x9a
io.ReadFull(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/io/io.go:351
google.golang.org/grpc/internal/transport.(*Stream).Read(0xc000448a20, {0xc000fcc298, 0x5, 0x5})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/transport.go:498 +0xac
google.golang.org/grpc.(*parser).recvMsg(0xc000fcc288, 0x10000000)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/rpc_util.go:597 +0x47
google.golang.org/grpc.recvAndDecompress(0x0?, 0xc000448a20, {0x0, 0x0}, 0x10000000, 0x0, {0x0, 0x0})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/rpc_util.go:729 +0x66
google.golang.org/grpc.recv(0x1575b60?, {0x7fb37cf547d8, 0x2373278}, 0x10b869d?, {0x0?, 0x0?}, {0x164a740, 0xc0007d4aa0}, 0xc0007d4830?, 0x0, ...)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/rpc_util.go:795 +0x6e
google.golang.org/grpc.(*serverStream).RecvMsg(0xc00074a000, {0x164a740?, 0xc0007d4aa0})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/stream.go:1699 +0x178
github.com/hashicorp/go-plugin/internal/plugin.(*gRPCBrokerStartStreamServer).Recv(0xc000d50340)
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/internal/plugin/grpc_broker.pb.go:167 +0x4c
github.com/hashicorp/go-plugin.(*gRPCBrokerServer).StartStream(0xc0006a2c60, {0x19179f0, 0xc000d50340})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/grpc_broker.go:87 +0x150
github.com/hashicorp/go-plugin/internal/plugin._GRPCBroker_StartStream_Handler({0x1552de0?, 0xc0006a2c60}, {0x1914810?, 0xc00074a000})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/internal/plugin/grpc_broker.pb.go:148 +0x9f
google.golang.org/grpc.(*Server).processStreamingRPC(0xc000545e00, {0x1918a40, 0xc000832340}, 0xc000448a20, 0xc0006a2cf0, 0x231dc20, 0x0)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:1639 +0x1384
google.golang.org/grpc.(*Server).handleStream(0xc000545e00, {0x1918a40, 0xc000832340}, 0xc000448a20, 0x0)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:1726 +0x9f0
google.golang.org/grpc.(*Server).serveStreams.func1.2()
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:966 +0x98
created by google.golang.org/grpc.(*Server).serveStreams.func1
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:964 +0x28a

goroutine 3580 [select]:
google.golang.org/grpc/internal/transport.(*recvBufferReader).read(0xc000fc68c0, {0xc000fcc838, 0x5, 0x5})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/transport.go:183 +0x90
google.golang.org/grpc/internal/transport.(*recvBufferReader).Read(0xc000fc68c0, {0xc000fcc838?, 0xc000f0a990?, 0xc00049e668?})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/transport.go:177 +0x178
google.golang.org/grpc/internal/transport.(*transportReader).Read(0xc0003fb2f0, {0xc000fcc838?, 0xc00049e6e0?, 0x10d3a67?})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/transport.go:514 +0x32
io.ReadAtLeast({0x1903b00, 0xc0003fb2f0}, {0xc000fcc838, 0x5, 0x5}, 0x5)
	/opt/hostedtoolcache/go/1.20.3/x64/src/io/io.go:332 +0x9a
io.ReadFull(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/io/io.go:351
google.golang.org/grpc/internal/transport.(*Stream).Read(0xc00015c360, {0xc000fcc838, 0x5, 0x5})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/transport.go:498 +0xac
google.golang.org/grpc.(*parser).recvMsg(0xc000fcc828, 0x10000000)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/rpc_util.go:597 +0x47
google.golang.org/grpc.recvAndDecompress(0xf46319?, 0xc00015c360, {0x0, 0x0}, 0x10000000, 0x0, {0x0, 0x0})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/rpc_util.go:729 +0x66
google.golang.org/grpc.recv(0xc0006fe900?, {0x7fb37cf547d8, 0x2373278}, 0x1642640?, {0x0?, 0x0?}, {0x164a740, 0xc0007d5400}, 0xc0006fe900?, 0x0, ...)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/rpc_util.go:795 +0x6e
google.golang.org/grpc.(*serverStream).RecvMsg(0xc00074a0f0, {0x164a740?, 0xc0007d5400})
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/stream.go:1699 +0x178
github.com/hashicorp/go-plugin/internal/plugin.(*gRPCBrokerStartStreamServer).Recv(0xc0012162c0)
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/internal/plugin/grpc_broker.pb.go:167 +0x4c
github.com/hashicorp/go-plugin.(*gRPCBrokerServer).StartStream(0xc0006a2c60, {0x19179f0, 0xc0012162c0})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/grpc_broker.go:87 +0x150
github.com/hashicorp/go-plugin/internal/plugin._GRPCBroker_StartStream_Handler({0x1552de0?, 0xc0006a2c60}, {0x1914810?, 0xc00074a0f0})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/internal/plugin/grpc_broker.pb.go:148 +0x9f
google.golang.org/grpc.(*Server).processStreamingRPC(0xc000545e00, {0x1918a40, 0xc00047c820}, 0xc00015c360, 0xc0006a2cf0, 0x231dc20, 0x0)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:1639 +0x1384
google.golang.org/grpc.(*Server).handleStream(0xc000545e00, {0x1918a40, 0xc00047c820}, 0xc00015c360, 0x0)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:1726 +0x9f0
google.golang.org/grpc.(*Server).serveStreams.func1.2()
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:966 +0x98
created by google.golang.org/grpc.(*Server).serveStreams.func1
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:964 +0x28a
internal/poll.(*pollDesc).wait(0xc000f0dd80?, 0xc00283e000?, 0x0)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:84 +0x32
internal/poll.(*pollDesc).waitRead(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc000f0dd80, {0xc00283e000, 0xa000, 0xa000})
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_unix.go:167 +0x299
net.(*netFD).Read(0xc000f0dd80, {0xc00283e000?, 0xc00283e060?, 0x33a?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/fd_posix.go:55 +0x29
net.(*conn).Read(0xc00048eea0, {0xc00283e000?, 0x444351?, 0xc00128f330?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/net.go:183 +0x45
crypto/tls.(*atLeastReader).Read(0xc000b7e228, {0xc00283e000?, 0xc000b7e228?, 0x0?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/crypto/tls/conn.go:788 +0x3d
bytes.(*Buffer).ReadFrom(0xc00128f410, {0x18ff040, 0xc000b7e228})
	/opt/hostedtoolcache/go/1.20.3/x64/src/bytes/buffer.go:202 +0x98
crypto/tls.(*Conn).readFromUntil(0xc00128f180, {0x1904520?, 0xc00048eea0}, 0x9fa5?)
	/opt/hostedtoolcache/go/1.20.3/x64/src/crypto/tls/conn.go:810 +0xe5
crypto/tls.(*Conn).readRecordOrCCS(0xc00128f180, 0x0)
	/opt/hostedtoolcache/go/1.20.3/x64/src/crypto/tls/conn.go:617 +0x116
crypto/tls.(*Conn).readRecord(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/crypto/tls/conn.go:583
crypto/tls.(*Conn).Read(0xc00128f180, {0xc0014e2000, 0x1000, 0x8ecc80?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/crypto/tls/conn.go:1316 +0x16f
bufio.(*Reader).Read(0xc00133fa40, {0xc0007184a0, 0x9, 0x90a905?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:237 +0x1bb
io.ReadAtLeast({0x18fed40, 0xc00133fa40}, {0xc0007184a0, 0x9, 0x9}, 0x9)
	/opt/hostedtoolcache/go/1.20.3/x64/src/io/io.go:332 +0x9a
io.ReadFull(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/io/io.go:351
net/http.http2readFrameHeader({0xc0007184a0?, 0x9?, 0xc0006a3800?}, {0x18fed40?, 0xc00133fa40?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/h2_bundle.go:1567 +0x6e
net/http.(*http2Framer).ReadFrame(0xc000718460)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/h2_bundle.go:1831 +0x95
net/http.(*http2clientConnReadLoop).run(0xc0003c8f98)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/h2_bundle.go:9187 +0x12e
net/http.(*http2ClientConn).readLoop(0xc00129cf00)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/h2_bundle.go:9082 +0x6f
created by net/http.(*http2Transport).newClientConn
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/h2_bundle.go:7779 +0xc3c

goroutine 3620 [select]:
google.golang.org/grpc/internal/transport.(*http2Server).keepalive(0xc00047c820)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/http2_server.go:[115](https://github.com/chainguard-dev/terraform-provider-apko/actions/runs/4916989168/jobs/8781471552?pr=52#step:7:116)0 +0x233
created by google.golang.org/grpc/internal/transport.NewServerTransport
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/http2_server.go:339 +0x1af8

goroutine 3072 [IO wait]:
internal/poll.runtime_pollWait(0x7fb37cfc[119](https://github.com/chainguard-dev/terraform-provider-apko/actions/runs/4916989168/jobs/8781471552?pr=52#step:7:120)0, 0x72)
	/opt/hostedtoolcache/go/1.20.3/x64/src/runtime/netpoll.go:306 +0x89
internal/poll.(*pollDesc).wait(0xc00053e600?, 0xc000370000?, 0x0)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:84 +0x32
internal/poll.(*pollDesc).waitRead(...)
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc00053e600, {0xc000370000, 0x1000, 0x1000})
	/opt/hostedtoolcache/go/1.20.3/x64/src/internal/poll/fd_unix.go:167 +0x299
net.(*netFD).Read(0xc00053e600, {0xc000370000?, 0x2?, 0x2329c00?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/fd_posix.go:55 +0x29
net.(*conn).Read(0xc00087c7d8, {0xc000370000?, 0x0?, 0x0?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/net.go:183 +0x45
net/http.(*persistConn).Read(0xc001218[120](https://github.com/chainguard-dev/terraform-provider-apko/actions/runs/4916989168/jobs/8781471552?pr=52#step:7:121), {0xc000370000?, 0x44d4e0?, 0xc0006a8ec8?})
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:1943 +0x4e
bufio.(*Reader).fill(0xc000da0cc0)
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:106 +0xff
bufio.(*Reader).Peek(0xc000da0cc0, 0x1)
	/opt/hostedtoolcache/go/1.20.3/x64/src/bufio/bufio.go:144 +0x5d
net/http.(*persistConn).readLoop(0xc00[121](https://github.com/chainguard-dev/terraform-provider-apko/actions/runs/4916989168/jobs/8781471552?pr=52#step:7:122)8120)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:2107 +0x1ac
created by net/http.(*Transport).dialConn
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:1765 +0x16ea

goroutine 3054 [select]:
net/http.(*persistConn).writeLoop(0xc00015c120)
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:2410 +0xf2
created by net/http.(*Transport).dialConn
	/opt/hostedtoolcache/go/1.20.3/x64/src/net/http/transport.go:1766 +0x173d

goroutine 3610 [select]:
github.com/hashicorp/terraform-exec/tfexec.writeOutput.func1()
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/tfexec/cmd.go:250 +0xa6
created by github.com/hashicorp/terraform-exec/tfexec.writeOutput
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/tfexec/cmd.go:249 +0x14a

goroutine 3619 [select]:
google.golang.org/grpc/internal/transport.(*controlBuffer).get(0xc000fc66e0, 0x1)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/controlbuf.go:417 +0x115
google.golang.org/grpc/internal/transport.(*loopyWriter).run(0xc0004d2850)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/controlbuf.go:549 +0x91
google.golang.org/grpc/internal/transport.NewServerTransport.func2()
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/http2_server.go:336 +0xce
created by google.golang.org/grpc/internal/transport.NewServerTransport
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/internal/transport/http2_server.go:333 +0x1ab3

goroutine 3577 [select]:
github.com/hashicorp/go-plugin.(*grpcStdioServer).StreamStdio(0xc000e557c0, 0x1587300?, {0x1916a80, 0xc000e55c50})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/grpc_stdio.go:61 +0x11d
github.com/hashicorp/go-plugin/internal/plugin._GRPCStdio_StreamStdio_Handler({0x14b4600?, 0xc000e557c0}, {0x1914810, 0xc0006040f0})
	/home/runner/go/pkg/mod/github.com/hashicorp/[email protected]/internal/plugin/grpc_stdio.pb.go:185 +0xd0
google.golang.org/grpc.(*Server).processStreamingRPC(0xc000545e00, {0x1918a40, 0xc000832340}, 0xc000239e60, 0xc0006a2ea0, 0x231dc40, 0x0)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:1639 +0x[138](https://github.com/chainguard-dev/terraform-provider-apko/actions/runs/4916989168/jobs/8781471552?pr=52#step:7:139)4
google.golang.org/grpc.(*Server).handleStream(0xc000545e00, {0x1918a40, 0xc000832340}, 0xc000239e60, 0x0)
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:[172](https://github.com/chainguard-dev/terraform-provider-apko/actions/runs/4916989168/jobs/8781471552?pr=52#step:7:173)6 +0x9f0
google.golang.org/grpc.(*Server).serveStreams.func1.2()
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:966 +0x98
created by google.golang.org/grpc.(*Server).serveStreams.func1
	/home/runner/go/pkg/mod/google.golang.org/[email protected]/server.go:964 +0x28a

GetRepositoryIndexes with ignoreSignatures=true does nothing

When calling GetRepositoryIndexes and disabling signature checking:

indexes, err := apk.GetRepositoryIndexes(ctx, repositories, keys, "x86_64", apk.WithIgnoreSignatures(true))

the function will never return any indexes.

In the index.go file, the indexes list is only ever appended to if signature validation is enabled and completes successfully (see here). This means that when signature validation is disabled, the indexes list will never be appended to and makes this entire function essentially a no-op.

I'm happy to open an MR to fix it if needed.

Package resolution failure: buffer overflow during APK index scan

Description of bug

The default buffer size of 64 KiB is not enough to read some lines in APKINDEX files, leading the index to be truncated.

As a result, package resolution for any package after truncation fails as it appears to be missing from the index.

This currently impacts the edge/testing index from Alpine:
https://dl-cdn.alpinelinux.org/alpine/edge/testing

The creation of the buffered scanner is found here:

indexScanner := bufio.NewScanner(apkIndexUnpacked)

Go documentation tells us that there is a default buffer size of 64 KiB unless otherwise configured:
https://pkg.go.dev/bufio#Scanner.Buffer
https://pkg.go.dev/bufio#MaxScanTokenSize

Investigation and surrounding circumstances

Resolution worked as expected until 2024-03-23, when a new version of the coq package was published to edge/testing:
https://pkgs.alpinelinux.org/package/edge/testing/aarch64/coq

I noticed resolution worked if I switched to the 386 architecture — this is because coq is not available for that architecture, so we sidestep the issue.
https://pkgs.alpinelinux.org/packages?name=coq&branch=edge&repo=testing&arch=x86

The resulting index entry for coq in APKINDEX lists dependencies on a line that is 76.2 KiB, exceeding the default 64 KiB limit:
https://dl-cdn.alpinelinux.org/alpine/edge/testing/aarch64/APKINDEX.tar.gz

Because the line is longer than the default token size, the scanner ends and the for loop exits. No error is thrown. As a result, the index is truncated, ending with the last package before coq (in this case it was copyq-doc). While there should have been over 6,000 packages in edge/testing, only about 550 were listed.

For this reason, the package I was resolving appeared to be missing from the index and apko failed.

Proposed fix

Configure a larger buffer for the buffered scanner to account for unexpectedly long lines. Adding two lines below the scanner increases the buffer to 1 MiB and fixes the issue — the apko file is now able to resolve:

// pkg/apk/apkindex.go
indexScanner := bufio.NewScanner(apkIndexUnpacked)

// Increase max token size from default of 64 KiB to account for large dependency lists.
maxTokenSize := 1 << 20 // 1 MiB
indexScanner.Buffer(make([]byte, maxTokenSize), maxTokenSize)

This is the permalink to the code in question:

indexScanner := bufio.NewScanner(apkIndexUnpacked)

There is similar logic, not on the critical path for this reproduction, that should likely be updated as well:

indexScanner := bufio.NewScanner(installed)

Minimal reproduction

Write the following contents to a file named apko.yaml:

contents:
  repositories:
    - https://dl-cdn.alpinelinux.org/alpine/edge/main
    - https://dl-cdn.alpinelinux.org/alpine/edge/testing
  packages:
    - librespot
archs:
  - arm64

Then try to resolve and observe the failure (with apko 0.14.0):

$ apko show-packages apko.yaml
ℹ️            | loading config file: apko.yaml
ℹ️            | Determining packages for 1 architectures: [arm64]
ℹ️            | loading config file: apko.yaml
ℹ️  aarch64   | doing pre-flight checks
ℹ️  aarch64   | initializing apk database
ℹ️  aarch64   | finished initializing apk database
ℹ️  aarch64   | setting apk repositories
ℹ️  aarch64   | initializing apk keyring
ℹ️  aarch64   | setting apk world
ℹ️  aarch64   | using working directory /var/folders/pt/b0dbzm7x7s1cq2splzq095y40000gn/T/apko-1910105790/aarch64
ℹ️  aarch64   | determining desired apk world
Error: failed to get package list for image: resolving apk packages: could not find package that provides librespot in indexes
ℹ️            | error during command execution: failed to get package list for image: resolving apk packages: could not find package that provides librespot in indexes

Affected versions and host environments

I've tested apko at version 0.13.2, version 0.14.0 (output above), and at HEAD when downloading the main branch from https://github.com/chainguard-dev/apko. All versions are impacted.

This error occurs both on macOS (ARM64v8 / Apple Silicon) and Linux (AMD64).

Next steps

I will send a PR shortly with the proposed fix.

A new version of go-apk would then have to be published, and the apko module would have to upgrade to that version. We'd then need a new version of apko to be published.

https://github.com/chainguard-dev/apko/blob/64231f0b36d888a28066c3bedf4dae39bc4b1870/go.mod#L9

go-apk allows for creating packages with invalid versions

I came across a slightly obscure bug, or at least I think it's a bug. I was building a package with melange that contained a version string with dashes and I noticed that alpine's apk would fail with package file format error on a specific case: A sub package with a provides = line containing an invalid version string. apk seemed happy to install packages with invalid versions in them but fail on packages with invalid versions in the .PKGINFO header file. In my case I found this happen on a provides = cmd:foo-$BAD_VERSION line, but it could happen on depends = lines too.

In our case the version string we were using was roughly: 8.0.0.20231201-ps-f1234567

Here is a reproducible example. I'll use the zlib.yaml from the melange/os repo. It doesn't matter the package, I just needed something quick to compile for the reproduction example.

I have made two changes to zlib.yaml from the original melange version:

  1. version modified to add -f12345678. Adding anything containing a dash is the key here.
  2. modified the dev subpackage to add a "binary" /usr/bin/foo, since the issue does not seem to appear until apk attempts to parse the provides = cmd:foo[VERSION] line of the dev package's .PKGINFO file.
package:
  name: zlib
  version: "1.3-f12345678"   # <---------------------- MODIFIED from original
  epoch: 0
  description: "a library implementing the zlib compression algorithms"
  copyright:
    - license: MPL-2.0 AND MIT

environment:
  contents:
    repositories:
      - https://packages.wolfi.dev/os
    keyring:
      - https://packages.wolfi.dev/os/wolfi-signing.rsa.pub
    packages:
      - autoconf
      - automake
      - build-base
      - busybox
      - ca-certificates-bundle
      - libtool
      - wolfi-baselayout

pipeline:
  - uses: fetch
    with:
      #uri: https://zlib.net/zlib-${{package.version}}.tar.gz
      uri: https://zlib.net/zlib-1.3.tar.gz
      expected-sha256: ff0ba4c292013dbc27530b3a81e1f9a813cd39de01ca5e0f8bf355702efa593e

  - runs: |
      CHOST="${{host.triplet.gnu}}" ./configure \
        --prefix=/usr \
        --libdir=/lib \
        --shared

  - uses: autoconf/make

  - runs: |
      make install pkgconfigdir="/usr/lib/pkgconfig" DESTDIR="${{targets.destdir}}"

  - uses: strip

subpackages:
  - name: "zlib-static"
    description: "zlib static library"
    pipeline:
      - uses: split/static

  - name: "zlib-dev"
    description: "zlib development headers"
    pipeline:
      - uses: split/dev
      # ------------ ADDED /usr/bin/foo to ensure a `provides = cmd:foo[VERSION` line is added to the dev package's .PKGINFO FILE: ------------
      - runs: |
          mkdir -p ${{targets.subpkgdir}}/usr/bin
          echo '#!/bin/sh' >${{targets.subpkgdir}}/usr/bin/test-foo
          echo 'echo hello' >>${{targets.subpkgdir}}/usr/bin/test-foo
          chmod +x ${{targets.subpkgdir}}/usr/bin/test-foo
    dependencies:
      runtime:
        - zlib

  - name: "minizip"
    description: "a library for manipulation with files from .zip archives"
    pipeline:
      - runs: |
          cd contrib/minizip
          autoreconf -fiv
          ./configure --prefix=/usr \
            --enable-static=no
          make
          make install DESTDIR="${{targets.contextdir}}"
melange keygen
melange build --arch host --source-dir . -k melange.rsa ./zlib.yaml
$ docker run --rm -it -v$PWD:/pkg cgr.dev/chainguard/sdkapk add -s -v --allow-untrusted /pkg/packages/x86_64/zlib-dev-1.3-f12345678-r0.apk

ERROR: /pkg/packages/x86_64/zlib-dev-1.3-f12345678-r0.apk: package file format error

Modify the zlib.yaml and set version: 1.3 and the dev package will work fine.

I am not sure if this bug should be in this repo or the melange repo, nor what the exact fix should be, other than to match the version validation logic from https://gitlab.alpinelinux.org/alpine/apk-tools/-/blob/2.14-stable/src/version.c

EDIT: After further testing it appears that only a single _ is allowed in the version string. The same version validation failure will occur on a version string like version: 1.3_f12345678_foo

Stack overflow caused by infinite recursion w/ symlinks

It looks like both getNode and OpenFile can recurse infinitely if they're given a path that participates in any kind of symlink cycle.

There are a few possible directions for fixing. cc: @deitch who might have preferences regarding the design...

  1. We can choose to prevent symlink cycles from the get-go in the Symlink method, and/or return an error from getNode and OpenFile when they detect a symlink cycle, given that one's been allowed to exist in the memFS object.

  2. Given that the current library design uses recursion, if we implement the second part of the above, we'd need to keep track of visited link targets in the stack, which means adding a parameter to the affected methods. I started to do this for getNode, but hesitated a bit on OpenFile since this would impact an exported method signature, and actually an entire interface method signature.

Steps to reproduce

m := NewMemFS()
err := m.Symlink("/a", "/a")
// ...
_, err = m.ReadFile("/a") // <-- this will panic!

(The fix should also include a test that uses the above steps to prove the fix.)

Panic calling APK.isInstalledPackage concurrently

https://github.com/chainguard-dev/go-apk/blob/main/pkg/apk/implementation.go#L536-L539

Saw this here:

goroutine 37444 [running]:
chainguard.dev/apko/pkg/tarfs.(*memFile).Read(0xc05366c3c0, {0xc0543be000?, 0xc05366c380?, 0x40?})
	chainguard.dev/[email protected]/pkg/tarfs/fs.go:823 +0x9b
bufio.(*Scanner).Scan(0xc05416fd30)
	bufio/scan.go:214 +0x876
github.com/chainguard-dev/go-apk/pkg/apk.parseInstalled({0x7fe48c50ea78, 0xc05366c3c0})
	github.com/chainguard-dev/[email protected]/pkg/apk/installed.go:275 +0x1b2
github.com/chainguard-dev/go-apk/pkg/apk.(*APK).GetInstalled(0xc3622660e0)
	github.com/chainguard-dev/[email protected]/pkg/apk/installed.go:49 +0x1b2
github.com/chainguard-dev/go-apk/pkg/apk.(*APK).isInstalledPackage(0x0?, {0xc0325af3f2, 0x5})
	github.com/chainguard-dev/[email protected]/pkg/apk/installed.go:105 +0x27
github.com/chainguard-dev/go-apk/pkg/apk.(*APK).FixateWorld.func2()
	github.com/chainguard-dev/[email protected]/pkg/apk/implementation.go:536 +0x88
golang.org/x/sync/errgroup.(*Group).Go.func1()
	golang.org/x/[email protected]/errgroup/errgroup.go:75 +0x64
created by golang.org/x/sync/errgroup.(*Group).Go
	golang.org/x/[email protected]/errgroup/errgroup.go:72 +0xa5

http scheme for repository is not supported

Hi! I'm trying to adopt melange and apko for our build processes and found an issue which stops me from that. It is not currently possible to use apk repository with http scheme, index just ignored.

I found that http is ignored in

switch asURL.Scheme {
, is it by design? I'm going to submit PR but not sure that you'll accept it because of insecure scheme:) Is it ok to also use insecure channel?

pkgfs doesn't support stat correctly

pkgfs should support stat as it allows for setuid/setgid linters to work properly such as here. This would help with package auditing and looking for setuid binaries where there should probably be none, which could be a security risk.

signature.RSASignSHA1Digest supports PKCS1 keys only

Alpine linux distribution recommends to sign packages with keys generated by abuild-keygen tool.

This tool, as of today, generates PKCS8 keys only (source).

Meanwhile signature.RSASignSHA1Digest can parse only PKCS1, so any attempt to sign something with PKCS8 key will result in error. Can you please fix that?

Also, golang documentations shows that PEM encryption-related functions are deprecated. So it'll likely be removed in the future. abuild-keygen can't generate encrypted keys at all.

Solver Troubles

The current solver logic is described here:

go-apk/pkg/apk/repo.go

Lines 417 to 444 in f86aaf2

// getPackageDependencies get all of the dependencies for a single package based on the
// indexes. Internal version includes passed arg for preventing infinite loops.
// checked map is passed as an arg, rather than a member of the struct, because
// it is unique to each lookup.
//
// The logic for dependencies in order is:
// 1. deeper before shallower
// 2. order of presentation
//
// for 2 dependencies at the same level, it is the first before the second
// for 2 dependencies one parent to the other, is is the child before the parent
//
// this means the logic for walking the tree is depth-first, down before across
// to do this correctly, we also need to handle duplicates and loops.
// For example
//
// A -> B -> C -> D
// -> C -> D
//
// We do not want to get C or D twice, or even have it appear on the list twice.
// The final result should include each of A,B,C,D exactly once, and in the correct order.
// That order should be: D, C, B, A
// The initial run will be D,C,B,D,C,A, which then should get simplified to D,C,B,A
// In addition, we need to ensure that we don't loop, for example, if D should point somehow to B
// or itself. We need a "checked" list that says, "already got the one this is pointing at".
// It might change the order of install.
// In other words, this _should_ be a DAG (acyclical), but because the packages
// are just listing dependencies in text, it might be cyclical. We need to be careful of that.

We start by independently resolving each package in our requirements list to the most recent version that satisfies the version constraints:

go-apk/pkg/apk/repo.go

Lines 268 to 279 in f86aaf2

// first get the explicitly named packages
for _, pkgName := range packages {
pkgs, err := p.ResolvePackage(pkgName)
if err != nil {
return nil, nil, err
}
if len(pkgs) == 0 {
return nil, nil, fmt.Errorf("could not find package %s", pkgName)
}
// do not add it to toInstall, as we want to have it in the correct order with dependencies
dependenciesMap[pkgs[0].Name] = pkgs[0]
}

We then greedily resolve dependencies for each of those packages:

pkg, deps, confs, err := p.GetPackageWithDependencies(pkgName, dependenciesMap)

This has some issues.

Example

Let's say our requirements list looks like this:

packages:
 - foo~1

And our index looks something like this:

P:foo
V:1.0.0-r0
D:bar baz~1

P:foo
V:2.0.0-r0
D:bar baz

P:bar
V:1.0.0-r0
D:baz

P:bar
V:2.0.0-r0
D:baz

P:baz
V:1.0.0-r0

P:baz
V:2.0.0-r0

Our first pass will resolve to:

packages:
 - foo=1.0.0-r0

Then we will walk our deps and resolve them like so:

  • foo~1
    • foo-1.0.0-r0
      • bar
        • bar-2.0.0-r0
          • baz
            • baz-2.0.0-r0
      • baz~1
        • baz-1.0.0-r0

As we collapse the stack we end up with a list of foo~1's deps:

  • foo~1
    • foo-1.0.0-r0
      • bar-2.0.0-r0
      • baz-2.0.0-r0
      • baz-1.0.0-r0

Then we eliminate duplicate dependencies in our deps, we will take the first baz we encountered (baz-2.0.0-r0):

go-apk/pkg/apk/repo.go

Lines 336 to 344 in f86aaf2

// eliminate duplication in dependencies
added := make(map[string]*RepositoryPackage, len(deps))
dependencies := make([]*RepositoryPackage, 0, len(deps))
for _, dep := range deps {
if _, ok := added[dep.Name]; !ok {
dependencies = append(dependencies, dep)
added[dep.Name] = dep
}
}

Further collapsing we end up with:

  • foo-1.0.0-r0
  • bar-2.0.0-r0
  • baz-2.0.0-r0

So we effectively ignore the baz~1 constraint.

Solutions

Actually solving this problem is pretty hard: https://research.swtch.com/version-sat

I think we can partially deal with this with two small changes to our solver.

1. Always fail on conflicts

See also chainguard-dev/apko#921

Right now we just ignore conflicts, which means we don't even know when we get into a bad situation.

2. Accumulate constraints

As we walk the dependency graph, we only consider the constraints of a single package at a time when we filterPackages.

Instead, what if we accumulate and pass down all the sibling requirements whenever we are resolving a package and call filterPackages multiple times for each requirement (keyed by the dep name).

This is still a greedy algorithm that won't do any backtracking, but at least we have a bit more control over what's going on.
If we fail to solve because the greedy solution can't satisfy all the constraints, the config author would be able to add additional constraints to steer the solution.

Something vaguely like:
image

So when we are solving foo-1.0.0-r0, we add baz~1 to our constraints map.
Later, when we are solving bar-2.0.0-r0's dependency on baz, we combine that with the baz~1 constraint.

Again, this isn't a general solution because the baz~1 constraint might not conveniently be a sibling of the bar dep, but it will help.

cannot handle multiple packages that have the same `cmd:` in `provide`

chainguard-dev/melange#1188 added code to melange that allowed symlinks to provide 'cmd:'

The justification for that is if a package provides /usr/bin/gcloud that is a symlink to /usr/lib/somewhere/gcloud, then the end result is that the package does provide cmd:gcloud and the user should be able to apk search cmd:gcloud to find it.

There was fallout of that change and it had to be reverted under chainguard-dev/melange#1200

The errors that showed up were (from a slack thread)

Two of them have errors during the apk test disqualified because ... already provides cmd:

We do ultimately

  1. want to have symlinks able to provide 'cmd:' (for the 'gcloud' suggestion above and also for dash-binsh package, which provides /bin/sh)
  2. need to have apk and go-apk have the same behavior.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.