Vendor c/common:8483ef6022b4

This commit vendor pre-release version of `c/common:8483ef6022b4`.
It also adapts the code to the new `c/common/libimage` API, which
fixes an image listing race that was listing false warnings.

fixes: #23331

Signed-off-by: Jan Rodák <hony.com@seznam.cz>
This commit is contained in:
Jan Rodák
2024-08-29 16:41:30 +02:00
parent f013ebe447
commit c6fe5e5395
175 changed files with 11890 additions and 1980 deletions

View File

@ -31,12 +31,12 @@ type ImageDiskUsage struct {
// storage. Note that a single image may yield multiple usage reports, one for
// each repository tag.
func (r *Runtime) DiskUsage(ctx context.Context) ([]ImageDiskUsage, int64, error) {
images, err := r.ListImages(ctx, nil, nil)
images, layers, err := r.getImagesAndLayers()
if err != nil {
return nil, -1, err
}
layerTree, err := r.layerTree(ctx, images)
layerTree, err := r.newLayerTreeFromData(images, layers)
if err != nil {
return nil, -1, err
}

View File

@ -19,13 +19,16 @@ import (
// filterFunc is a prototype for a positive image filter. Returning `true`
// indicates that the image matches the criteria.
type filterFunc func(*Image) (bool, error)
type filterFunc func(*Image, *layerTree) (bool, error)
type compiledFilters map[string][]filterFunc
// Apply the specified filters. All filters of each key must apply.
func (i *Image) applyFilters(ctx context.Context, filters map[string][]filterFunc) (bool, error) {
// tree must be provided if compileImageFilters indicated it is necessary.
func (i *Image) applyFilters(ctx context.Context, filters compiledFilters, tree *layerTree) (bool, error) {
for key := range filters {
for _, filter := range filters[key] {
matches, err := filter(i)
matches, err := filter(i, tree)
if err != nil {
// Some images may have been corrupted in the
// meantime, so do an extra check and make the
@ -47,18 +50,11 @@ func (i *Image) applyFilters(ctx context.Context, filters map[string][]filterFun
// filterImages returns a slice of images which are passing all specified
// filters.
func (r *Runtime) filterImages(ctx context.Context, images []*Image, options *ListImagesOptions) ([]*Image, error) {
if len(options.Filters) == 0 || len(images) == 0 {
return images, nil
}
filters, err := r.compileImageFilters(ctx, options)
if err != nil {
return nil, err
}
// tree must be provided if compileImageFilters indicated it is necessary.
func (r *Runtime) filterImages(ctx context.Context, images []*Image, filters compiledFilters, tree *layerTree) ([]*Image, error) {
result := []*Image{}
for i := range images {
match, err := images[i].applyFilters(ctx, filters)
match, err := images[i].applyFilters(ctx, filters, tree)
if err != nil {
return nil, err
}
@ -73,25 +69,19 @@ func (r *Runtime) filterImages(ctx context.Context, images []*Image, options *Li
// required format is `key=value` with the following supported keys:
//
// after, since, before, containers, dangling, id, label, readonly, reference, intermediate
func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOptions) (map[string][]filterFunc, error) {
//
// compileImageFilters returns: compiled filters, if LayerTree is needed, error
func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOptions) (compiledFilters, bool, error) {
logrus.Tracef("Parsing image filters %s", options.Filters)
var tree *layerTree
getTree := func() (*layerTree, error) {
if tree == nil {
t, err := r.layerTree(ctx, nil)
if err != nil {
return nil, err
}
tree = t
}
return tree, nil
if len(options.Filters) == 0 {
return nil, false, nil
}
filterInvalidValue := `invalid image filter %q: must be in the format "filter=value or filter!=value"`
var wantedReferenceMatches, unwantedReferenceMatches []string
filters := map[string][]filterFunc{}
filters := compiledFilters{}
needsLayerTree := false
duplicate := map[string]string{}
for _, f := range options.Filters {
var key, value string
@ -103,7 +93,7 @@ func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOp
} else {
split = strings.SplitN(f, "=", 2)
if len(split) != 2 {
return nil, fmt.Errorf(filterInvalidValue, f)
return nil, false, fmt.Errorf(filterInvalidValue, f)
}
}
@ -113,7 +103,7 @@ func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOp
case "after", "since":
img, err := r.time(key, value)
if err != nil {
return nil, err
return nil, false, err
}
key = "since"
filter = filterAfter(img.Created())
@ -121,27 +111,23 @@ func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOp
case "before":
img, err := r.time(key, value)
if err != nil {
return nil, err
return nil, false, err
}
filter = filterBefore(img.Created())
case "containers":
if err := r.containers(duplicate, key, value, options.IsExternalContainerFunc); err != nil {
return nil, err
return nil, false, err
}
filter = filterContainers(value, options.IsExternalContainerFunc)
case "dangling":
dangling, err := r.bool(duplicate, key, value)
if err != nil {
return nil, err
return nil, false, err
}
t, err := getTree()
if err != nil {
return nil, err
}
filter = filterDangling(ctx, dangling, t)
needsLayerTree = true
filter = filterDangling(ctx, dangling)
case "id":
filter = filterID(value)
@ -149,35 +135,31 @@ func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOp
case "digest":
f, err := filterDigest(value)
if err != nil {
return nil, err
return nil, false, err
}
filter = f
case "intermediate":
intermediate, err := r.bool(duplicate, key, value)
if err != nil {
return nil, err
return nil, false, err
}
t, err := getTree()
if err != nil {
return nil, err
}
filter = filterIntermediate(ctx, intermediate, t)
needsLayerTree = true
filter = filterIntermediate(ctx, intermediate)
case "label":
filter = filterLabel(ctx, value)
case "readonly":
readOnly, err := r.bool(duplicate, key, value)
if err != nil {
return nil, err
return nil, false, err
}
filter = filterReadOnly(readOnly)
case "manifest":
manifest, err := r.bool(duplicate, key, value)
if err != nil {
return nil, err
return nil, false, err
}
filter = filterManifest(ctx, manifest)
@ -192,12 +174,12 @@ func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOp
case "until":
until, err := r.until(value)
if err != nil {
return nil, err
return nil, false, err
}
filter = filterBefore(until)
default:
return nil, fmt.Errorf(filterInvalidValue, key)
return nil, false, fmt.Errorf(filterInvalidValue, key)
}
if negate {
filter = negateFilter(filter)
@ -210,12 +192,12 @@ func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOp
filter := filterReferences(r, wantedReferenceMatches, unwantedReferenceMatches)
filters["reference"] = append(filters["reference"], filter)
return filters, nil
return filters, needsLayerTree, nil
}
func negateFilter(f filterFunc) filterFunc {
return func(img *Image) (bool, error) {
b, err := f(img)
return func(img *Image, tree *layerTree) (bool, error) {
b, err := f(img, tree)
return !b, err
}
}
@ -272,7 +254,7 @@ func (r *Runtime) bool(duplicate map[string]string, key, value string) (bool, er
// filterManifest filters whether or not the image is a manifest list
func filterManifest(ctx context.Context, value bool) filterFunc {
return func(img *Image) (bool, error) {
return func(img *Image, _ *layerTree) (bool, error) {
isManifestList, err := img.IsManifestList(ctx)
if err != nil {
return false, err
@ -284,7 +266,7 @@ func filterManifest(ctx context.Context, value bool) filterFunc {
// filterReferences creates a reference filter for matching the specified wantedReferenceMatches value (OR logic)
// and for matching the unwantedReferenceMatches values (AND logic)
func filterReferences(r *Runtime, wantedReferenceMatches, unwantedReferenceMatches []string) filterFunc {
return func(img *Image) (bool, error) {
return func(img *Image, _ *layerTree) (bool, error) {
// Empty reference filters, return true
if len(wantedReferenceMatches) == 0 && len(unwantedReferenceMatches) == 0 {
return true, nil
@ -376,7 +358,7 @@ func imageMatchesReferenceFilter(r *Runtime, img *Image, value string) (bool, er
// filterLabel creates a label for matching the specified value.
func filterLabel(ctx context.Context, value string) filterFunc {
return func(img *Image) (bool, error) {
return func(img *Image, _ *layerTree) (bool, error) {
labels, err := img.Labels(ctx)
if err != nil {
return false, err
@ -387,28 +369,28 @@ func filterLabel(ctx context.Context, value string) filterFunc {
// filterAfter creates an after filter for matching the specified value.
func filterAfter(value time.Time) filterFunc {
return func(img *Image) (bool, error) {
return func(img *Image, _ *layerTree) (bool, error) {
return img.Created().After(value), nil
}
}
// filterBefore creates a before filter for matching the specified value.
func filterBefore(value time.Time) filterFunc {
return func(img *Image) (bool, error) {
return func(img *Image, _ *layerTree) (bool, error) {
return img.Created().Before(value), nil
}
}
// filterReadOnly creates a readonly filter for matching the specified value.
func filterReadOnly(value bool) filterFunc {
return func(img *Image) (bool, error) {
return func(img *Image, _ *layerTree) (bool, error) {
return img.IsReadOnly() == value, nil
}
}
// filterContainers creates a container filter for matching the specified value.
func filterContainers(value string, fn IsExternalContainerFunc) filterFunc {
return func(img *Image) (bool, error) {
return func(img *Image, _ *layerTree) (bool, error) {
ctrs, err := img.Containers()
if err != nil {
return false, err
@ -433,8 +415,8 @@ func filterContainers(value string, fn IsExternalContainerFunc) filterFunc {
}
// filterDangling creates a dangling filter for matching the specified value.
func filterDangling(ctx context.Context, value bool, tree *layerTree) filterFunc {
return func(img *Image) (bool, error) {
func filterDangling(ctx context.Context, value bool) filterFunc {
return func(img *Image, tree *layerTree) (bool, error) {
isDangling, err := img.isDangling(ctx, tree)
if err != nil {
return false, err
@ -445,7 +427,7 @@ func filterDangling(ctx context.Context, value bool, tree *layerTree) filterFunc
// filterID creates an image-ID filter for matching the specified value.
func filterID(value string) filterFunc {
return func(img *Image) (bool, error) {
return func(img *Image, _ *layerTree) (bool, error) {
return strings.HasPrefix(img.ID(), value), nil
}
}
@ -455,7 +437,7 @@ func filterDigest(value string) (filterFunc, error) {
if !strings.HasPrefix(value, "sha256:") {
return nil, fmt.Errorf("invalid value %q for digest filter", value)
}
return func(img *Image) (bool, error) {
return func(img *Image, _ *layerTree) (bool, error) {
return img.containsDigestPrefix(value), nil
}, nil
}
@ -463,8 +445,8 @@ func filterDigest(value string) (filterFunc, error) {
// filterIntermediate creates an intermediate filter for images. An image is
// considered to be an intermediate image if it is dangling (i.e., no tags) and
// has no children (i.e., no other image depends on it).
func filterIntermediate(ctx context.Context, value bool, tree *layerTree) filterFunc {
return func(img *Image) (bool, error) {
func filterIntermediate(ctx context.Context, value bool) filterFunc {
return func(img *Image, tree *layerTree) (bool, error) {
isIntermediate, err := img.isIntermediate(ctx, tree)
if err != nil {
return false, err

View File

@ -25,7 +25,7 @@ func (i *Image) History(ctx context.Context) ([]ImageHistory, error) {
return nil, err
}
layerTree, err := i.runtime.layerTree(ctx, nil)
layerTree, err := i.runtime.newFreshLayerTree()
if err != nil {
return nil, err
}

View File

@ -11,7 +11,7 @@ import (
"strings"
"time"
"github.com/containerd/containerd/platforms"
"github.com/containerd/platforms"
"github.com/containers/common/libimage/platform"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/manifest"
@ -257,7 +257,7 @@ func (i *Image) TopLayer() string {
// Parent returns the parent image or nil if there is none
func (i *Image) Parent(ctx context.Context) (*Image, error) {
tree, err := i.runtime.layerTree(ctx, nil)
tree, err := i.runtime.newFreshLayerTree()
if err != nil {
return nil, err
}
@ -291,7 +291,7 @@ func (i *Image) Children(ctx context.Context) ([]*Image, error) {
// created for this invocation only.
func (i *Image) getChildren(ctx context.Context, all bool, tree *layerTree) ([]*Image, error) {
if tree == nil {
t, err := i.runtime.layerTree(ctx, nil)
t, err := i.runtime.newFreshLayerTree()
if err != nil {
return nil, err
}

View File

@ -3,7 +3,6 @@
package libimage
import (
"context"
"fmt"
"strings"
@ -38,7 +37,7 @@ func (i *Image) Tree(traverseChildren bool) (string, error) {
fmt.Fprintf(sb, "No Image Layers")
}
layerTree, err := i.runtime.layerTree(context.Background(), nil)
layerTree, err := i.runtime.newFreshLayerTree()
if err != nil {
return "", err
}

View File

@ -89,21 +89,18 @@ func (l *layerNode) repoTags() ([]string, error) {
return orderedTags, nil
}
// layerTree extracts a layerTree from the layers in the local storage and
// relates them to the specified images.
func (r *Runtime) layerTree(ctx context.Context, images []*Image) (*layerTree, error) {
layers, err := r.store.Layers()
// newFreshLayerTree extracts a layerTree from consistent layers and images in the local storage.
func (r *Runtime) newFreshLayerTree() (*layerTree, error) {
images, layers, err := r.getImagesAndLayers()
if err != nil {
return nil, err
}
return r.newLayerTreeFromData(images, layers)
}
if images == nil {
images, err = r.ListImages(ctx, nil, nil)
if err != nil {
return nil, err
}
}
// newLayerTreeFromData extracts a layerTree from the given the layers and images.
// The caller is responsible for (layers, images) being consistent.
func (r *Runtime) newLayerTreeFromData(images []*Image, layers []storage.Layer) (*layerTree, error) {
tree := layerTree{
nodes: make(map[string]*layerNode),
ociCache: make(map[string]*ociv1.Image),

View File

@ -139,7 +139,7 @@ func (m *ManifestList) LookupInstance(ctx context.Context, architecture, os, var
return nil, err
}
allImages, err := m.image.runtime.ListImages(ctx, nil, nil)
allImages, err := m.image.runtime.ListImages(ctx, nil)
if err != nil {
return nil, err
}

View File

@ -4,7 +4,7 @@ import (
"fmt"
"runtime"
"github.com/containerd/containerd/platforms"
"github.com/containerd/platforms"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
)

View File

@ -161,6 +161,23 @@ func (r *Runtime) storageToImage(storageImage *storage.Image, ref types.ImageRef
}
}
// getImagesAndLayers obtains consistent slices of Image and storage.Layer
func (r *Runtime) getImagesAndLayers() ([]*Image, []storage.Layer, error) {
snapshot, err := r.store.MultiList(
storage.MultiListOptions{
Images: true,
Layers: true,
})
if err != nil {
return nil, nil, err
}
images := []*Image{}
for i := range snapshot.Images {
images = append(images, r.storageToImage(&snapshot.Images[i], nil))
}
return images, snapshot.Layers, nil
}
// Exists returns true if the specified image exists in the local containers
// storage. Note that it may return false if an image corrupted.
func (r *Runtime) Exists(name string) (bool, error) {
@ -479,7 +496,7 @@ func (r *Runtime) lookupImageInDigestsAndRepoTags(name string, possiblyUnqualifi
return nil, "", fmt.Errorf("%s: %w (could not cast to tagged)", originalName, storage.ErrImageUnknown)
}
allImages, err := r.ListImages(context.Background(), nil, nil)
allImages, err := r.ListImages(context.Background(), nil)
if err != nil {
return nil, "", err
}
@ -567,39 +584,46 @@ type ListImagesOptions struct {
SetListData bool
}
// ListImages lists images in the local container storage. If names are
// specified, only images with the specified names are looked up and filtered.
func (r *Runtime) ListImages(ctx context.Context, names []string, options *ListImagesOptions) ([]*Image, error) {
// ListImagesByNames lists the images in the local container storage by specified names
// The name lookups use the LookupImage semantics.
func (r *Runtime) ListImagesByNames(names []string) ([]*Image, error) {
images := []*Image{}
for _, name := range names {
image, _, err := r.LookupImage(name, nil)
if err != nil {
return nil, err
}
images = append(images, image)
}
return images, nil
}
// ListImages lists the images in the local container storage and filter the images by ListImagesOptions
func (r *Runtime) ListImages(ctx context.Context, options *ListImagesOptions) ([]*Image, error) {
if options == nil {
options = &ListImagesOptions{}
}
var images []*Image
if len(names) > 0 {
for _, name := range names {
image, _, err := r.LookupImage(name, nil)
if err != nil {
return nil, err
}
images = append(images, image)
}
} else {
storageImages, err := r.store.Images()
if err != nil {
return nil, err
}
for i := range storageImages {
images = append(images, r.storageToImage(&storageImages[i], nil))
}
}
filtered, err := r.filterImages(ctx, images, options)
filters, needsLayerTree, err := r.compileImageFilters(ctx, options)
if err != nil {
return nil, err
}
if !options.SetListData {
return filtered, nil
if options.SetListData {
needsLayerTree = true
}
snapshot, err := r.store.MultiList(
storage.MultiListOptions{
Images: true,
Layers: needsLayerTree,
})
if err != nil {
return nil, err
}
images := []*Image{}
for i := range snapshot.Images {
images = append(images, r.storageToImage(&snapshot.Images[i], nil))
}
// If explicitly requested by the user, pre-compute and cache the
@ -608,11 +632,23 @@ func (r *Runtime) ListImages(ctx context.Context, names []string, options *ListI
// as the layer tree will computed once for all instead of once for
// each individual image (see containers/podman/issues/17828).
tree, err := r.layerTree(ctx, images)
var tree *layerTree
if needsLayerTree {
tree, err = r.newLayerTreeFromData(images, snapshot.Layers)
if err != nil {
return nil, err
}
}
filtered, err := r.filterImages(ctx, images, filters, tree)
if err != nil {
return nil, err
}
if !options.SetListData {
return filtered, nil
}
for i := range filtered {
isDangling, err := filtered[i].isDangling(ctx, tree)
if err != nil {
@ -753,7 +789,7 @@ func (r *Runtime) RemoveImages(ctx context.Context, names []string, options *Rem
IsExternalContainerFunc: options.IsExternalContainerFunc,
Filters: options.Filters,
}
filteredImages, err := r.ListImages(ctx, nil, options)
filteredImages, err := r.ListImages(ctx, options)
if err != nil {
appendError(err)
return nil, rmErrors

View File

@ -1,6 +1,4 @@
//go:build (linux || freebsd) && cni
// +build linux freebsd
// +build cni
package network

View File

@ -1,6 +1,4 @@
//go:build (linux || freebsd) && !cni
// +build linux freebsd
// +build !cni
package network

View File

@ -1,4 +1,4 @@
//go:build (freebsd || netbsd || openbsd)
//go:build freebsd || netbsd || openbsd
package config

View File

@ -1,4 +1,4 @@
//go:build (freebsd || netbsd || openbsd)
//go:build freebsd || netbsd || openbsd
package config