mirror of
https://github.com/containers/podman.git
synced 2025-08-14 19:12:48 +08:00

Listing images has shown increasing performance penalties with an increasing number of images. Unless `--all` is specified, Podman will filter intermediate images. Determining intermediate images has been done by finding (and comparing!) parent images which is expensive. We had to query the storage many times which turned it into a bottleneck. Instead, create a layer tree and assign one or more images to nodes that match the images' top layer. Determining the children of an image is now exponentially faster as we already know the child images from the layer graph and the images using the same top layer, which may also be considered child images based on their history. On my system with 510 images, a rootful image list drops from 6 secs down to 0.3 secs. Also use the tree to compute parent nodes, and to filter intermediate images for pruning. Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
119 lines
3.7 KiB
Go
119 lines
3.7 KiB
Go
package utils
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/containers/image/v5/docker"
|
|
"github.com/containers/image/v5/storage"
|
|
"github.com/containers/image/v5/transports/alltransports"
|
|
"github.com/containers/image/v5/types"
|
|
"github.com/containers/podman/v2/libpod"
|
|
"github.com/containers/podman/v2/libpod/image"
|
|
"github.com/gorilla/schema"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// ParseDockerReference parses the specified image name to a
|
|
// `types.ImageReference` and enforces it to refer to a docker-transport
|
|
// reference.
|
|
func ParseDockerReference(name string) (types.ImageReference, error) {
|
|
dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name())
|
|
imageRef, err := alltransports.ParseImageName(name)
|
|
if err == nil && imageRef.Transport().Name() != docker.Transport.Name() {
|
|
return nil, errors.Errorf("reference %q must be a docker reference", name)
|
|
} else if err != nil {
|
|
origErr := err
|
|
imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, name))
|
|
if err != nil {
|
|
return nil, errors.Wrapf(origErr, "reference %q must be a docker reference", name)
|
|
}
|
|
}
|
|
return imageRef, nil
|
|
}
|
|
|
|
// ParseStorageReference parses the specified image name to a
|
|
// `types.ImageReference` and enforces it to refer to a
|
|
// containers-storage-transport reference.
|
|
func ParseStorageReference(name string) (types.ImageReference, error) {
|
|
storagePrefix := fmt.Sprintf("%s:", storage.Transport.Name())
|
|
imageRef, err := alltransports.ParseImageName(name)
|
|
if err == nil && imageRef.Transport().Name() != docker.Transport.Name() {
|
|
return nil, errors.Errorf("reference %q must be a storage reference", name)
|
|
} else if err != nil {
|
|
origErr := err
|
|
imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", storagePrefix, name))
|
|
if err != nil {
|
|
return nil, errors.Wrapf(origErr, "reference %q must be a storage reference", name)
|
|
}
|
|
}
|
|
return imageRef, nil
|
|
}
|
|
|
|
// GetImages is a common function used to get images for libpod and other compatibility
|
|
// mechanisms
|
|
func GetImages(w http.ResponseWriter, r *http.Request) ([]*image.Image, error) {
|
|
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
|
query := struct {
|
|
All bool
|
|
Filters map[string][]string `schema:"filters"`
|
|
Digests bool
|
|
Filter string // Docker 1.24 compatibility
|
|
}{
|
|
// This is where you can override the golang default value for one of fields
|
|
}
|
|
|
|
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
|
return nil, err
|
|
}
|
|
var filters = []string{}
|
|
if _, found := r.URL.Query()["digests"]; found && query.Digests {
|
|
UnSupportedParameter("digests")
|
|
}
|
|
var (
|
|
images []*image.Image
|
|
err error
|
|
)
|
|
|
|
queryFilters := query.Filters
|
|
if !IsLibpodRequest(r) && len(query.Filter) > 0 { // Docker 1.24 compatibility
|
|
if queryFilters == nil {
|
|
queryFilters = make(map[string][]string)
|
|
}
|
|
queryFilters["reference"] = append(queryFilters["reference"], query.Filter)
|
|
}
|
|
|
|
if len(queryFilters) > 0 {
|
|
for k, v := range queryFilters {
|
|
filters = append(filters, fmt.Sprintf("%s=%s", k, strings.Join(v, "=")))
|
|
}
|
|
images, err = runtime.ImageRuntime().GetImagesWithFilters(filters)
|
|
if err != nil {
|
|
return images, err
|
|
}
|
|
} else {
|
|
images, err = runtime.ImageRuntime().GetImages()
|
|
if err != nil {
|
|
return images, err
|
|
}
|
|
}
|
|
if query.All {
|
|
return images, nil
|
|
}
|
|
|
|
filter, err := runtime.ImageRuntime().IntermediateFilter(r.Context(), images)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
images = image.FilterImages(images, []image.ResultFilter{filter})
|
|
|
|
return images, nil
|
|
}
|
|
|
|
func GetImage(r *http.Request, name string) (*image.Image, error) {
|
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
|
return runtime.ImageRuntime().NewFromLocal(name)
|
|
}
|