mirror of
https://github.com/anchore/syft.git
synced 2026-11-01 23:16:30 +01:00
Add container-storage support for Podman and Buildah (#5368)
* feat: add containers-storage support for Podman and Buildah, update tests and documentation Signed-off-by: Bruce Clark <bruce.clark@mandg.com> * test: update containers-storage tests to validate package.json and handle missing images Signed-off-by: Bruce Clark <bruce.clark@mandg.com> * test: fix rootless containers-storage test setup Install uidmap for Podman and Buildah CI jobs. Align temporary rootful and rootless storage paths. Clean up rootless stores within the builder's user namespace. Signed-off-by: Bruce Clark <bruce.clark@mandg.com> * fix: only use containers-storage when explicitly requested - containers-storage is no longer part of automatic image resolution; it's used only for `--from containers-storage`, the `containers-storage:` scheme, or `default-pull-source: containers-storage` (now an accepted value). Opening a store runs full graph driver init (mounts, locks, store writes), which is too invasive as a probe on every plain image reference. - rootless stores must be read from inside the builder's user namespace (`podman unshare syft ...`), documented in the README. - the `containers_image_openpgp` tag is now applied to unit/integration tests and lint, so dev tooling compiles the same provider that ships. - containers-storage CI runs through `make containers-storage-test` and covers both vfs and overlay stores. Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix: exclude cgo btrfs driver and unmount overlay store in cleanup - cgo-enabled linux builds (`-race` tests, lint) compile go.podman.io's btrfs graph driver, which needs libbtrfs headers. Add `exclude_graphdriver_btrfs` alongside `containers_image_openpgp` everywhere the tag is set; release builds are `CGO_ENABLED=0` and never included it. - the overlay driver bind-mounts its graphroot inside the rootless user namespace, so the containers-storage test unmounts it before removing the store, and cleanup no longer fails the run. Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> --------- Signed-off-by: Bruce Clark <bruce.clark@mandg.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> Co-authored-by: Bruce Clark <bruce.clark@mandg.com> Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
co-authored by
Bruce Clark
Alex Goodman
parent
6c9bb732dd
commit
29b92a58c1
@@ -3,6 +3,7 @@ package syft
|
||||
import (
|
||||
"crypto"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/anchore/go-collections"
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
@@ -10,6 +11,11 @@ import (
|
||||
"github.com/anchore/syft/syft/source/sourceproviders"
|
||||
)
|
||||
|
||||
// GetSourceConfig controls which source providers are tried, and in what order, when resolving user input to a source.
|
||||
//
|
||||
// The "containers-storage" provider (local Buildah / Podman store) is never part of automatic resolution: it is only
|
||||
// used when named in Sources or DefaultImagePullSource. It is also only functional when built with the
|
||||
// containers_image_openpgp build tag (as syft's Linux release binaries are); otherwise it always returns an error.
|
||||
type GetSourceConfig struct {
|
||||
// SourceProviderConfig may optionally be provided to be used when constructing the default set of source providers, unused if All specified
|
||||
SourceProviderConfig *sourceproviders.Config
|
||||
@@ -64,6 +70,12 @@ func (c *GetSourceConfig) WithDefaultImagePullSource(defaultImagePullSource stri
|
||||
func (c *GetSourceConfig) getProviders(userInput string) ([]source.Provider, error) {
|
||||
providers := collections.TaggedValueSet[source.Provider]{}.Join(sourceproviders.All(userInput, c.SourceProviderConfig)...)
|
||||
|
||||
// opening a containers-storage store runs full graph driver init (mounts, locks, possible store writes), which is
|
||||
// too invasive for an "is this image local?" probe on every plain image reference. Only use it when asked by name.
|
||||
if !c.requestsSource(image.ContainersStorageSource) {
|
||||
providers = providers.Remove(image.ContainersStorageSource)
|
||||
}
|
||||
|
||||
// if the "default image pull source" is set, we move this as the first pull source
|
||||
if c.DefaultImagePullSource != "" {
|
||||
base := providers.Remove(sourceproviders.PullTag)
|
||||
@@ -84,6 +96,10 @@ func (c *GetSourceConfig) getProviders(userInput string) ([]source.Provider, err
|
||||
return providers.Values(), nil
|
||||
}
|
||||
|
||||
func (c *GetSourceConfig) requestsSource(name string) bool {
|
||||
return c.DefaultImagePullSource == name || slices.Contains(c.Sources, name)
|
||||
}
|
||||
|
||||
func DefaultGetSourceConfig() *GetSourceConfig {
|
||||
return &GetSourceConfig{
|
||||
SourceProviderConfig: sourceproviders.DefaultConfig(),
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
package syft
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/stereoscope"
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/source/sourceproviders"
|
||||
)
|
||||
|
||||
@@ -17,8 +23,9 @@ func TestGetProviders_DefaultImagePullSource(t *testing.T) {
|
||||
t.Errorf("Expected no error for DefaultImagePullSource parameter, got: %v", err)
|
||||
}
|
||||
|
||||
if len(providers) != len(allSourceProviders) {
|
||||
t.Errorf("Expected %d providers, got %d", len(allSourceProviders), len(providers))
|
||||
// everything except containers-storage, which is only used when requested by name
|
||||
if len(providers) != len(allSourceProviders)-1 {
|
||||
t.Errorf("Expected %d providers, got %d", len(allSourceProviders)-1, len(providers))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,3 +43,55 @@ func TestGetProviders_Sources(t *testing.T) {
|
||||
t.Errorf("Expected 2 providers, got %d", len(providers))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetProviders_ContainersStorageOnlyWhenRequested(t *testing.T) {
|
||||
const storage = image.ContainersStorageSource
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg *GetSourceConfig
|
||||
wantFound bool
|
||||
// if set, containers-storage must be ordered ahead of this provider
|
||||
wantBefore string
|
||||
}{
|
||||
{
|
||||
name: "excluded from automatic resolution",
|
||||
cfg: DefaultGetSourceConfig(),
|
||||
},
|
||||
{
|
||||
name: "excluded when selecting all pull sources",
|
||||
cfg: DefaultGetSourceConfig().WithSources(sourceproviders.PullTag),
|
||||
},
|
||||
{
|
||||
name: "excluded when a different default pull source is set",
|
||||
cfg: DefaultGetSourceConfig().WithDefaultImagePullSource(stereoscope.RegistryTag),
|
||||
},
|
||||
{
|
||||
name: "included when explicitly selected",
|
||||
cfg: DefaultGetSourceConfig().WithSources(storage),
|
||||
wantFound: true,
|
||||
},
|
||||
{
|
||||
name: "included ahead of other pull sources when it is the default pull source",
|
||||
cfg: DefaultGetSourceConfig().WithDefaultImagePullSource(storage),
|
||||
wantFound: true,
|
||||
wantBefore: image.DockerDaemonSource,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
providers, err := tt.cfg.getProviders("localhost/myimage:latest")
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, providers)
|
||||
|
||||
assert.Equal(t, tt.wantFound, providerIndex(providers, storage) >= 0)
|
||||
if tt.wantBefore != "" {
|
||||
assert.Less(t, providerIndex(providers, storage), providerIndex(providers, tt.wantBefore))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func providerIndex(providers []source.Provider, name string) int {
|
||||
return slices.IndexFunc(providers, func(p source.Provider) bool { return p.Name() == name })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user