diff --git a/go.mod b/go.mod index 6daf055adff..68721b73cf6 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.12 require ( github.com/containernetworking/cni v0.8.1 - github.com/containers/common v0.40.2-0.20210707094508-0a4a1906d4b2 + github.com/containers/common v0.41.1-0.20210721112610-c95d2f794edf github.com/containers/image/v5 v5.13.2 github.com/containers/ocicrypt v1.1.2 github.com/containers/storage v1.32.6 @@ -21,7 +21,7 @@ require ( github.com/onsi/gomega v1.14.0 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 - github.com/opencontainers/runc v1.0.0 + github.com/opencontainers/runc v1.0.1 github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 github.com/opencontainers/runtime-tools v0.9.0 github.com/opencontainers/selinux v1.8.2 diff --git a/go.sum b/go.sum index 10c6acd8fa4..ce7eb6c7477 100644 --- a/go.sum +++ b/go.sum @@ -137,6 +137,7 @@ github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.6.1/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -224,8 +225,8 @@ github.com/containernetworking/cni v0.8.1 h1:7zpDnQ3T3s4ucOuJ/ZCLrYBxzkg0AELFfII github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= -github.com/containers/common v0.40.2-0.20210707094508-0a4a1906d4b2 h1:2ApmOS9jSnJXuSOkZNEAZ7j0/9i8zjoi67b/UpUjPxY= -github.com/containers/common v0.40.2-0.20210707094508-0a4a1906d4b2/go.mod h1:thow5Jn7O+rP01njI9COQ16L9g/KQ1LcMcYqP2NhYCU= +github.com/containers/common v0.41.1-0.20210721112610-c95d2f794edf h1:z0ciG0ByyJG3WCBpLYd2XLThCC7UBaH7GeSfXY4sAqc= +github.com/containers/common v0.41.1-0.20210721112610-c95d2f794edf/go.mod h1:Ba5YVNCnyX6xDtg1JqEHa2EMVMW5UbHmIyEqsEwpeGE= github.com/containers/image/v5 v5.13.2 h1:AgYunV/9d2fRkrmo23wH2MkqeHolFd6oQCkK+1PpuFA= github.com/containers/image/v5 v5.13.2/go.mod h1:GkWursKDlDcUIT7L7vZf70tADvZCk/Ga0wgS0MuF0ag= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE= @@ -236,7 +237,6 @@ github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B github.com/containers/ocicrypt v1.1.2 h1:Ez+GAMP/4GLix5Ywo/fL7O0nY771gsBIigiqUm1aXz0= github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= github.com/containers/storage v1.32.2/go.mod h1:YIBxxjfXZTi04Ah49sh1uSGfmT1V89+I5i3deRobzQo= -github.com/containers/storage v1.32.5/go.mod h1:8/DVVDqniaUlUV0D0q7cEnXK6Bs2uU3FPqNZVPumwEs= github.com/containers/storage v1.32.6 h1:NqdFRewXO/PYPjgCAScoigZc5QUA21yapSEj6kqD8cw= github.com/containers/storage v1.32.6/go.mod h1:mdB+b89p+jU8zpzLTVXA0gWMmIo0WrkfGMh1R8O2IQw= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -611,7 +611,6 @@ github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= @@ -620,7 +619,6 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= github.com/onsi/gomega v1.14.0 h1:ep6kpPVwmr/nTbklSx2nrLNSIO62DoYAhnPNIMhK8gI= github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -639,8 +637,9 @@ github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= github.com/opencontainers/runc v1.0.0-rc95/go.mod h1:z+bZxa/+Tz/FmYVWkhUajJdzFeOqjc5vrqskhVyHGUM= -github.com/opencontainers/runc v1.0.0 h1:QOhAQAYUlKeofuyeKdR6ITvOnXLPbEAjPMjz9wCUXcU= github.com/opencontainers/runc v1.0.0/go.mod h1:MU2S3KEB2ZExnhnAQYbwjdYV6HwKtDlNbA2Z2OeNDeA= +github.com/opencontainers/runc v1.0.1 h1:G18PGckGdAm3yVQRWDVQ1rLSLntiniKJ0cNRT2Tm5gs= +github.com/opencontainers/runc v1.0.1/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= diff --git a/tests/rmi.bats b/tests/rmi.bats index 8570e7f0ecb..061d29ec0bf 100644 --- a/tests/rmi.bats +++ b/tests/rmi.bats @@ -155,6 +155,21 @@ load helpers expect_output "" } +@test "attempt to prune non-dangling empty images" { + # Regression test for containers/podman/issues/10832 + ctxdir=${TESTDIR}/bud + mkdir -p $ctxdir + cat >$ctxdir/Dockerfile < 0 { + return false, nil + } + children, err := i.getChildren(ctx, false) + if err != nil { + return false, err + } + return len(children) == 0, nil } // IsIntermediate returns true if the image is an intermediate image, that is -// a dangling image without children. +// an untagged image with children. func (i *Image) IsIntermediate(ctx context.Context) (bool, error) { - // If the image has tags, it's not an intermediate one. - if !i.IsDangling() { + if len(i.Names()) > 0 { return false, nil } children, err := i.getChildren(ctx, false) if err != nil { return false, err } - // No tags, no children -> intermediate! return len(children) != 0, nil } @@ -271,7 +276,7 @@ type RemoveImageReport struct { // remove removes the image along with all dangling parent images that no other // image depends on. The image must not be set read-only and not be used by -// containers. +// containers. Returns IDs of removed/untagged images in order. // // If the image is used by containers return storage.ErrImageUsedByContainer. // Use force to remove these containers. @@ -282,7 +287,12 @@ type RemoveImageReport struct { // // This function is internal. Users of libimage should always use // `(*Runtime).RemoveImages()`. -func (i *Image) remove(ctx context.Context, rmMap map[string]*RemoveImageReport, referencedBy string, options *RemoveImagesOptions) error { +func (i *Image) remove(ctx context.Context, rmMap map[string]*RemoveImageReport, referencedBy string, options *RemoveImagesOptions) ([]string, error) { + processedIDs := []string{} + return i.removeRecursive(ctx, rmMap, processedIDs, referencedBy, options) +} + +func (i *Image) removeRecursive(ctx context.Context, rmMap map[string]*RemoveImageReport, processedIDs []string, referencedBy string, options *RemoveImagesOptions) ([]string, error) { // If referencedBy is empty, the image is considered to be removed via // `image remove --all` which alters the logic below. @@ -294,7 +304,7 @@ func (i *Image) remove(ctx context.Context, rmMap map[string]*RemoveImageReport, logrus.Debugf("Removing image %s", i.ID()) if i.IsReadOnly() { - return errors.Errorf("cannot remove read-only image %q", i.ID()) + return processedIDs, errors.Errorf("cannot remove read-only image %q", i.ID()) } if i.runtime.eventChannel != nil { @@ -306,7 +316,7 @@ func (i *Image) remove(ctx context.Context, rmMap map[string]*RemoveImageReport, if exists { // If the image has already been removed, we're done. if report.Removed { - return nil + return processedIDs, nil } } else { report = &RemoveImageReport{ID: i.ID()} @@ -333,7 +343,7 @@ func (i *Image) remove(ctx context.Context, rmMap map[string]*RemoveImageReport, if options.WithSize { size, err := i.Size() if handleError(err) != nil { - return err + return processedIDs, err } report.Size = size } @@ -354,18 +364,18 @@ func (i *Image) remove(ctx context.Context, rmMap map[string]*RemoveImageReport, byDigest := strings.HasPrefix(referencedBy, "sha256:") if !options.Force { if byID && numNames > 1 { - return errors.Errorf("unable to delete image %q by ID with more than one tag (%s): please force removal", i.ID(), i.Names()) + return processedIDs, errors.Errorf("unable to delete image %q by ID with more than one tag (%s): please force removal", i.ID(), i.Names()) } else if byDigest && numNames > 1 { // FIXME - Docker will remove the digest but containers storage // does not support that yet, so our hands are tied. - return errors.Errorf("unable to delete image %q by digest with more than one tag (%s): please force removal", i.ID(), i.Names()) + return processedIDs, errors.Errorf("unable to delete image %q by digest with more than one tag (%s): please force removal", i.ID(), i.Names()) } } // Only try to untag if we know it's not an ID or digest. if !byID && !byDigest { if err := i.Untag(referencedBy); handleError(err) != nil { - return err + return processedIDs, err } report.Untagged = append(report.Untagged, referencedBy) @@ -374,14 +384,15 @@ func (i *Image) remove(ctx context.Context, rmMap map[string]*RemoveImageReport, } } + processedIDs = append(processedIDs, i.ID()) if skipRemove { - return nil + return processedIDs, nil } // Perform the actual removal. First, remove containers if needed. if options.Force { if err := i.removeContainers(options.RemoveContainerFunc); err != nil { - return err + return processedIDs, err } } @@ -407,7 +418,7 @@ func (i *Image) remove(ctx context.Context, rmMap map[string]*RemoveImageReport, } if _, err := i.runtime.store.DeleteImage(i.ID(), true); handleError(err) != nil { - return err + return processedIDs, err } report.Untagged = append(report.Untagged, i.Names()...) @@ -417,27 +428,24 @@ func (i *Image) remove(ctx context.Context, rmMap map[string]*RemoveImageReport, // Check if can remove the parent image. if parent == nil { - return nil + return processedIDs, nil } - if !parent.IsDangling() { - return nil - } - - // If the image has siblings, we don't remove the parent. - hasSiblings, err := parent.HasChildren(ctx) + // Only remove the parent if it's dangling, that is being untagged and + // without children. + danglingParent, err := parent.IsDangling(ctx) if err != nil { // See Podman commit fd9dd7065d44: we need to // be tolerant toward corrupted images. logrus.Warnf("error determining if an image is a parent: %v, ignoring the error", err) - hasSiblings = false + danglingParent = false } - if hasSiblings { - return nil + if !danglingParent { + return processedIDs, nil } // Recurse into removing the parent. - return parent.remove(ctx, rmMap, "", options) + return parent.removeRecursive(ctx, rmMap, processedIDs, "", options) } // Tag the image with the specified name and store it in the local containers diff --git a/vendor/github.com/containers/common/libimage/image_tree.go b/vendor/github.com/containers/common/libimage/image_tree.go index b8b9cb21605..d48aeeada34 100644 --- a/vendor/github.com/containers/common/libimage/image_tree.go +++ b/vendor/github.com/containers/common/libimage/image_tree.go @@ -80,6 +80,10 @@ func (i *Image) Tree(traverseChildren bool) (string, error) { } func imageTreeTraverseChildren(node *layerNode, parent gotree.Tree) error { + if node.layer == nil { + return nil + } + var tags string repoTags, err := node.repoTags() if err != nil { diff --git a/vendor/github.com/containers/common/libimage/layer_tree.go b/vendor/github.com/containers/common/libimage/layer_tree.go index 4195b43c0e7..05f21531b0a 100644 --- a/vendor/github.com/containers/common/libimage/layer_tree.go +++ b/vendor/github.com/containers/common/libimage/layer_tree.go @@ -15,6 +15,9 @@ type layerTree struct { // ociCache is a cache for Image.ID -> OCI Image. Translations are done // on-demand. ociCache map[string]*ociv1.Image + // emptyImages do not have any top-layer so we cannot create a + // *layerNode for them. + emptyImages []*Image } // node returns a layerNode for the specified layerID. @@ -105,6 +108,7 @@ func (r *Runtime) layerTree() (*layerTree, error) { img := images[i] // do not leak loop variable outside the scope topLayer := img.TopLayer() if topLayer == "" { + tree.emptyImages = append(tree.emptyImages, img) continue } node, exists := tree.nodes[topLayer] @@ -126,22 +130,13 @@ func (r *Runtime) layerTree() (*layerTree, error) { // either the same top layer as parent or parent being the true parent layer. // Furthermore, the history of the parent and child images must match with the // parent having one history item less. If all is true, all images are -// returned. Otherwise, the first image is returned. +// returned. Otherwise, the first image is returned. Note that manifest lists +// do not have children. func (t *layerTree) children(ctx context.Context, parent *Image, all bool) ([]*Image, error) { if parent.TopLayer() == "" { - return nil, nil - } - - var children []*Image - - parentNode, exists := t.nodes[parent.TopLayer()] - if !exists { - // Note: erroring out in this case has turned out having been a - // mistake. Users may not be able to recover, so we're now - // throwing a warning to guide them to resolve the issue and - // turn the errors non-fatal. - logrus.Warnf("Layer %s not found in layer tree. The storage may be corrupted, consider running `podman system reset`.", parent.TopLayer()) - return children, nil + if isManifestList, _ := parent.IsManifestList(ctx); isManifestList { + return nil, nil + } } parentID := parent.ID() @@ -163,6 +158,38 @@ func (t *layerTree) children(ctx context.Context, parent *Image, all bool) ([]*I return areParentAndChild(parentOCI, childOCI), nil } + var children []*Image + + // Empty images are special in that they do not have any physical layer + // but yet can have a parent-child relation. Hence, compare the + // "parent" image to all other known empty images. + if parent.TopLayer() == "" { + for i := range t.emptyImages { + empty := t.emptyImages[i] + isParent, err := checkParent(empty) + if err != nil { + return nil, err + } + if isParent { + children = append(children, empty) + if !all { + break + } + } + } + return children, nil + } + + parentNode, exists := t.nodes[parent.TopLayer()] + if !exists { + // Note: erroring out in this case has turned out having been a + // mistake. Users may not be able to recover, so we're now + // throwing a warning to guide them to resolve the issue and + // turn the errors non-fatal. + logrus.Warnf("Layer %s not found in layer tree. The storage may be corrupted, consider running `podman system reset`.", parent.TopLayer()) + return children, nil + } + // addChildrenFrom adds child images of parent to children. Returns // true if any image is a child of parent. addChildrenFromNode := func(node *layerNode) (bool, error) { @@ -204,8 +231,37 @@ func (t *layerTree) children(ctx context.Context, parent *Image, all bool) ([]*I } // parent returns the parent image or nil if no parent image could be found. +// Note that manifest lists do not have parents. func (t *layerTree) parent(ctx context.Context, child *Image) (*Image, error) { if child.TopLayer() == "" { + if isManifestList, _ := child.IsManifestList(ctx); isManifestList { + return nil, nil + } + } + + childID := child.ID() + childOCI, err := t.toOCI(ctx, child) + if err != nil { + return nil, err + } + + // Empty images are special in that they do not have any physical layer + // but yet can have a parent-child relation. Hence, compare the + // "child" image to all other known empty images. + if child.TopLayer() == "" { + for _, empty := range t.emptyImages { + if childID == empty.ID() { + continue + } + emptyOCI, err := t.toOCI(ctx, empty) + if err != nil { + return nil, err + } + // History check. + if areParentAndChild(emptyOCI, childOCI) { + return empty, nil + } + } return nil, nil } @@ -219,14 +275,8 @@ func (t *layerTree) parent(ctx context.Context, child *Image) (*Image, error) { return nil, nil } - childOCI, err := t.toOCI(ctx, child) - if err != nil { - return nil, err - } - // Check images from the parent node (i.e., parent layer) and images // with the same layer (i.e., same top layer). - childID := child.ID() images := node.images if node.parent != nil { images = append(images, node.parent.images...) diff --git a/vendor/github.com/containers/common/libimage/pull.go b/vendor/github.com/containers/common/libimage/pull.go index fbe3b466df4..71cec021bc9 100644 --- a/vendor/github.com/containers/common/libimage/pull.go +++ b/vendor/github.com/containers/common/libimage/pull.go @@ -394,8 +394,23 @@ func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName str // very likely a bug but a consistent one in Podman/Buildah and should // be addressed at a later point. if pullPolicy != config.PullPolicyAlways { - logrus.Debugf("Enforcing pull policy to %q to support custom platform (arch: %q, os: %q, variant: %q)", "always", options.Architecture, options.OS, options.Variant) - pullPolicy = config.PullPolicyAlways + switch { + // User input clearly refer to a local image. + case strings.HasPrefix(imageName, "localhost/"): + logrus.Debugf("Enforcing pull policy to %q to support custom platform (arch: %q, os: %q, variant: %q)", "never", options.Architecture, options.OS, options.Variant) + pullPolicy = config.PullPolicyNever + + // Image resolved to a local one, so let's still have a + // look at the registries or aliases but use it + // otherwise. + case strings.HasPrefix(resolvedImageName, "localhost/"): + logrus.Debugf("Enforcing pull policy to %q to support custom platform (arch: %q, os: %q, variant: %q)", "newer", options.Architecture, options.OS, options.Variant) + pullPolicy = config.PullPolicyNewer + + default: + logrus.Debugf("Enforcing pull policy to %q to support custom platform (arch: %q, os: %q, variant: %q)", "always", options.Architecture, options.OS, options.Variant) + pullPolicy = config.PullPolicyAlways + } } } diff --git a/vendor/github.com/containers/common/libimage/runtime.go b/vendor/github.com/containers/common/libimage/runtime.go index 1fd2973cbc2..26a04dad58e 100644 --- a/vendor/github.com/containers/common/libimage/runtime.go +++ b/vendor/github.com/containers/common/libimage/runtime.go @@ -3,7 +3,6 @@ package libimage import ( "context" "os" - "path/filepath" "strings" "github.com/containers/image/v5/docker/reference" @@ -94,10 +93,6 @@ func RuntimeFromStore(store storage.Store, options *RuntimeOptions) (*Runtime, e setRegistriesConfPath(&systemContext) - if systemContext.BlobInfoCacheDir == "" { - systemContext.BlobInfoCacheDir = filepath.Join(store.GraphRoot(), "cache") - } - return &Runtime{ store: store, systemContext: systemContext, @@ -592,11 +587,10 @@ func (r *Runtime) RemoveImages(ctx context.Context, names []string, options *Rem rmErrors = append(rmErrors, err) } - orderedIDs := []string{} // determinism and relative order deleteMap := make(map[string]*deleteMe) // ID -> deleteMe - + toDelete := []string{} // Look up images in the local containers storage and fill out - // orderedIDs and the deleteMap. + // toDelete and the deleteMap. switch { case len(names) > 0: // Look up the images one-by-one. That allows for removing @@ -610,15 +604,12 @@ func (r *Runtime) RemoveImages(ctx context.Context, names []string, options *Rem } dm, exists := deleteMap[img.ID()] if !exists { - orderedIDs = append(orderedIDs, img.ID()) + toDelete = append(toDelete, img.ID()) dm = &deleteMe{image: img} deleteMap[img.ID()] = dm } dm.referencedBy = append(dm.referencedBy, resolvedName) } - if len(orderedIDs) == 0 { - return nil, rmErrors - } default: filteredImages, err := r.ListImages(ctx, nil, &ListImagesOptions{Filters: options.Filters}) @@ -627,14 +618,21 @@ func (r *Runtime) RemoveImages(ctx context.Context, names []string, options *Rem return nil, rmErrors } for _, img := range filteredImages { - orderedIDs = append(orderedIDs, img.ID()) + toDelete = append(toDelete, img.ID()) deleteMap[img.ID()] = &deleteMe{image: img} } } + // Return early if there's no image to delete. + if len(deleteMap) == 0 { + return nil, rmErrors + } + // Now remove the images in the given order. rmMap := make(map[string]*RemoveImageReport) - for _, id := range orderedIDs { + orderedIDs := []string{} + visitedIDs := make(map[string]bool) + for _, id := range toDelete { del, exists := deleteMap[id] if !exists { appendError(errors.Errorf("internal error: ID %s not in found in image-deletion map", id)) @@ -644,9 +642,17 @@ func (r *Runtime) RemoveImages(ctx context.Context, names []string, options *Rem del.referencedBy = []string{""} } for _, ref := range del.referencedBy { - if err := del.image.remove(ctx, rmMap, ref, options); err != nil { + processedIDs, err := del.image.remove(ctx, rmMap, ref, options) + if err != nil { appendError(err) - continue + } + // NOTE: make sure to add given ID only once to orderedIDs. + for _, id := range processedIDs { + if visited := visitedIDs[id]; visited { + continue + } + orderedIDs = append(orderedIDs, id) + visitedIDs[id] = true } } } diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go index 68076b1170d..84876026da2 100644 --- a/vendor/github.com/containers/common/pkg/config/config.go +++ b/vendor/github.com/containers/common/pkg/config/config.go @@ -158,6 +158,13 @@ type ContainersConfig struct { // PidNS indicates how to create a pid namespace for the container PidNS string `toml:"pidns,omitempty"` + // Copy the content from the underlying image into the newly created + // volume when the container is created instead of when it is started. + // If false, the container engine will not copy the content until + // the container is started. Setting it to true may have negative + // performance implications. + PrepareVolumeOnCreate bool `toml:"prepare_volume_on_create,omitempty"` + // RootlessNetworking depicts the "kind" of networking for rootless // containers. Valid options are `slirp4netns` and `cni`. Default is // `slirp4netns` @@ -384,6 +391,10 @@ type EngineConfig struct { // will refer to the plugin as) mapped to a path, which must point to a // Unix socket that conforms to the Volume Plugin specification. VolumePlugins map[string]string `toml:"volume_plugins,omitempty"` + + // ChownCopiedFiles tells the container engine whether to chown files copied + // into a container to the container's primary uid/gid. + ChownCopiedFiles bool `toml:"chown_copied_files"` } // SetOptions contains a subset of options in a Config. It's used to indicate if diff --git a/vendor/github.com/containers/common/pkg/config/containers.conf b/vendor/github.com/containers/common/pkg/config/containers.conf index 41f3c784a73..f429b96edb6 100644 --- a/vendor/github.com/containers/common/pkg/config/containers.conf +++ b/vendor/github.com/containers/common/pkg/config/containers.conf @@ -189,6 +189,13 @@ default_sysctls = [ # # pids_limit = 2048 +# Copy the content from the underlying image into the newly created volume +# when the container is created instead of when it is started. If false, +# the container engine will not copy the content until the container is started. +# Setting it to true may have negative performance implications. +# +# prepare_volume_on_create = false + # Indicates the networking to be used for rootless containers # rootless_networking = "slirp4netns" diff --git a/vendor/github.com/containers/common/pkg/config/default.go b/vendor/github.com/containers/common/pkg/config/default.go index 5abb6326f49..a16dd0e023c 100644 --- a/vendor/github.com/containers/common/pkg/config/default.go +++ b/vendor/github.com/containers/common/pkg/config/default.go @@ -340,6 +340,8 @@ func defaultConfigFromMemory() (*EngineConfig, error) { c.LockType = "shm" c.MachineEnabled = false + c.ChownCopiedFiles = true + return c, nil } diff --git a/vendor/github.com/containers/common/version/version.go b/vendor/github.com/containers/common/version/version.go index 8907e21ab92..47dca527c2f 100644 --- a/vendor/github.com/containers/common/version/version.go +++ b/vendor/github.com/containers/common/version/version.go @@ -1,4 +1,4 @@ package version // Version is the version of the build. -const Version = "0.40.2-dev" +const Version = "0.41.1-dev" diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/user.go b/vendor/github.com/opencontainers/runc/libcontainer/user/user.go index d2c16f7fd36..cc7a106be5d 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/user/user.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/user/user.go @@ -2,6 +2,7 @@ package user import ( "bufio" + "bytes" "errors" "fmt" "io" @@ -55,11 +56,11 @@ type IDMap struct { Count int64 } -func parseLine(line string, v ...interface{}) { - parseParts(strings.Split(line, ":"), v...) +func parseLine(line []byte, v ...interface{}) { + parseParts(bytes.Split(line, []byte(":")), v...) } -func parseParts(parts []string, v ...interface{}) { +func parseParts(parts [][]byte, v ...interface{}) { if len(parts) == 0 { return } @@ -75,16 +76,16 @@ func parseParts(parts []string, v ...interface{}) { // This is legit. switch e := v[i].(type) { case *string: - *e = p + *e = string(p) case *int: // "numbers", with conversion errors ignored because of some misbehaving configuration files. - *e, _ = strconv.Atoi(p) + *e, _ = strconv.Atoi(string(p)) case *int64: - *e, _ = strconv.ParseInt(p, 10, 64) + *e, _ = strconv.ParseInt(string(p), 10, 64) case *[]string: // Comma-separated lists. - if p != "" { - *e = strings.Split(p, ",") + if len(p) != 0 { + *e = strings.Split(string(p), ",") } else { *e = []string{} } @@ -128,8 +129,8 @@ func ParsePasswdFilter(r io.Reader, filter func(User) bool) ([]User, error) { ) for s.Scan() { - line := strings.TrimSpace(s.Text()) - if line == "" { + line := bytes.TrimSpace(s.Bytes()) + if len(line) == 0 { continue } @@ -179,15 +180,53 @@ func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) { if r == nil { return nil, fmt.Errorf("nil source for group-formatted data") } + rd := bufio.NewReader(r) + out := []Group{} - var ( - s = bufio.NewScanner(r) - out = []Group{} - ) + // Read the file line-by-line. + for { + var ( + isPrefix bool + wholeLine []byte + err error + ) - for s.Scan() { - text := s.Text() - if text == "" { + // Read the next line. We do so in chunks (as much as reader's + // buffer is able to keep), check if we read enough columns + // already on each step and store final result in wholeLine. + for { + var line []byte + line, isPrefix, err = rd.ReadLine() + + if err != nil { + // We should return no error if EOF is reached + // without a match. + if err == io.EOF { //nolint:errorlint // comparison with io.EOF is legit, https://github.com/polyfloyd/go-errorlint/pull/12 + err = nil + } + return out, err + } + + // Simple common case: line is short enough to fit in a + // single reader's buffer. + if !isPrefix && len(wholeLine) == 0 { + wholeLine = line + break + } + + wholeLine = append(wholeLine, line...) + + // Check if we read the whole line already. + if !isPrefix { + break + } + } + + // There's no spec for /etc/passwd or /etc/group, but we try to follow + // the same rules as the glibc parser, which allows comments and blank + // space at the beginning of a line. + wholeLine = bytes.TrimSpace(wholeLine) + if len(wholeLine) == 0 || wholeLine[0] == '#' { continue } @@ -197,17 +236,12 @@ func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) { // root:x:0:root // adm:x:4:root,adm,daemon p := Group{} - parseLine(text, &p.Name, &p.Pass, &p.Gid, &p.List) + parseLine(wholeLine, &p.Name, &p.Pass, &p.Gid, &p.List) if filter == nil || filter(p) { out = append(out, p) } } - if err := s.Err(); err != nil { - return nil, err - } - - return out, nil } type ExecUser struct { @@ -278,7 +312,7 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) ( // Allow for userArg to have either "user" syntax, or optionally "user:group" syntax var userArg, groupArg string - parseLine(userSpec, &userArg, &groupArg) + parseLine([]byte(userSpec), &userArg, &groupArg) // Convert userArg and groupArg to be numeric, so we don't have to execute // Atoi *twice* for each iteration over lines. @@ -496,8 +530,8 @@ func ParseSubIDFilter(r io.Reader, filter func(SubID) bool) ([]SubID, error) { ) for s.Scan() { - line := strings.TrimSpace(s.Text()) - if line == "" { + line := bytes.TrimSpace(s.Bytes()) + if len(line) == 0 { continue } @@ -549,14 +583,14 @@ func ParseIDMapFilter(r io.Reader, filter func(IDMap) bool) ([]IDMap, error) { ) for s.Scan() { - line := strings.TrimSpace(s.Text()) - if line == "" { + line := bytes.TrimSpace(s.Bytes()) + if len(line) == 0 { continue } // see: man 7 user_namespaces p := IDMap{} - parseParts(strings.Fields(line), &p.ID, &p.ParentID, &p.Count) + parseParts(bytes.Fields(line), &p.ID, &p.ParentID, &p.Count) if filter == nil || filter(p) { out = append(out, p) diff --git a/vendor/modules.txt b/vendor/modules.txt index 9a6cc8d0452..a5cde14d80e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -60,7 +60,7 @@ github.com/containernetworking/cni/pkg/types/020 github.com/containernetworking/cni/pkg/types/current github.com/containernetworking/cni/pkg/utils github.com/containernetworking/cni/pkg/version -# github.com/containers/common v0.40.2-0.20210707094508-0a4a1906d4b2 +# github.com/containers/common v0.41.1-0.20210721112610-c95d2f794edf github.com/containers/common/libimage github.com/containers/common/libimage/manifests github.com/containers/common/pkg/apparmor @@ -387,7 +387,7 @@ github.com/opencontainers/go-digest # github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 github.com/opencontainers/image-spec/specs-go github.com/opencontainers/image-spec/specs-go/v1 -# github.com/opencontainers/runc v1.0.0 +# github.com/opencontainers/runc v1.0.1 github.com/opencontainers/runc/libcontainer/apparmor github.com/opencontainers/runc/libcontainer/devices github.com/opencontainers/runc/libcontainer/user