refine dangling checks

By proxy by vendoring containers/common. Previously, a "dangling" image
was an untagged image; just a described in the Docker docs. The
definition of dangling has now been refined to an untagged image without
children to be compatible with Docker.

Further update a redundant image-prune test.

Fixes: #10998
Fixes: #10832
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
Valentin Rothberg
2021-07-19 11:41:04 +02:00
parent ec5c7c1f6a
commit 1b6423e9f1
24 changed files with 300 additions and 255 deletions

View File

@ -36,7 +36,7 @@ Filter output based on conditions provided
Filter on images created before the given IMAGE (name or tag). Filter on images created before the given IMAGE (name or tag).
**dangling=true|false** **dangling=true|false**
Show dangling images. Dangling images are a file system layer that was used in a previous build of an image and is no longer referenced by any active images. They are denoted with the `<none>` tag, consume disk space and serve no active purpose. Show dangling images. Dangling images are a file system layer that was used in a previous build of an image and is no longer referenced by any image. They are denoted with the `<none>` tag, consume disk space and serve no active purpose.
**label** **label**
Filter by images labels key and/or value. Filter by images labels key and/or value.

View File

@ -10,7 +10,7 @@ podman\-rmi - Removes one or more locally stored images
## DESCRIPTION ## DESCRIPTION
Removes one or more locally stored images. Removes one or more locally stored images.
Passing an argument _image_ deletes it, along with any of its dangling (untagged) parent images. Passing an argument _image_ deletes it, along with any of its dangling parent images. A dangling image is an image without a tag and without being referenced by another image.
## OPTIONS ## OPTIONS

4
go.mod
View File

@ -11,8 +11,8 @@ require (
github.com/container-orchestrated-devices/container-device-interface v0.0.0-20210325223243-f99e8b6c10b9 github.com/container-orchestrated-devices/container-device-interface v0.0.0-20210325223243-f99e8b6c10b9
github.com/containernetworking/cni v0.8.1 github.com/containernetworking/cni v0.8.1
github.com/containernetworking/plugins v0.9.1 github.com/containernetworking/plugins v0.9.1
github.com/containers/buildah v1.21.1-0.20210707133512-2eb97b499d74 github.com/containers/buildah v1.21.1-0.20210721171232-54cafea4c933
github.com/containers/common v0.41.1-0.20210716140645-ffcfe1ff6e70 github.com/containers/common v0.41.1-0.20210721172332-291287e9d060
github.com/containers/conmon v2.0.20+incompatible github.com/containers/conmon v2.0.20+incompatible
github.com/containers/image/v5 v5.13.2 github.com/containers/image/v5 v5.13.2
github.com/containers/ocicrypt v1.1.2 github.com/containers/ocicrypt v1.1.2

13
go.sum
View File

@ -239,11 +239,11 @@ github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHV
github.com/containernetworking/plugins v0.8.7/go.mod h1:R7lXeZaBzpfqapcAbHRW8/CYwm0dHzbz0XEjofx0uB0= github.com/containernetworking/plugins v0.8.7/go.mod h1:R7lXeZaBzpfqapcAbHRW8/CYwm0dHzbz0XEjofx0uB0=
github.com/containernetworking/plugins v0.9.1 h1:FD1tADPls2EEi3flPc2OegIY1M9pUa9r2Quag7HMLV8= github.com/containernetworking/plugins v0.9.1 h1:FD1tADPls2EEi3flPc2OegIY1M9pUa9r2Quag7HMLV8=
github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8=
github.com/containers/buildah v1.21.1-0.20210707133512-2eb97b499d74 h1:u+Wk1Gv+UgOR0AHmlpbH+ZsdfZt7vkGuiKCyhbI4Ga0= github.com/containers/buildah v1.21.1-0.20210721171232-54cafea4c933 h1:jqO3hDypBoKM5be+fVcqGHOpX2fOiQy2DFEeb/VKpsk=
github.com/containers/buildah v1.21.1-0.20210707133512-2eb97b499d74/go.mod h1:P0rxPkI7440rWVMkbPkSHX4j1PxjOcx+cY8QrqYgdLE= github.com/containers/buildah v1.21.1-0.20210721171232-54cafea4c933/go.mod h1:9gspFNeUJxIK72n1IMIKIHmtcePEZQsv0tjo+1LqkCo=
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/go.mod h1:Ba5YVNCnyX6xDtg1JqEHa2EMVMW5UbHmIyEqsEwpeGE=
github.com/containers/common v0.41.1-0.20210716140645-ffcfe1ff6e70 h1:9kKqg10PfrLQdsN4lw/n8XIuqLk43JwwAcE4klCvOsI= github.com/containers/common v0.41.1-0.20210721172332-291287e9d060 h1:HgGff2MeEKfYoKp2WQFl9xdsgP7KV8rr/1JZRIuPXmg=
github.com/containers/common v0.41.1-0.20210716140645-ffcfe1ff6e70/go.mod h1:w0CLPB8nH75msgISbE/z8TMIxK9disFsmvFNd2SDid8= github.com/containers/common v0.41.1-0.20210721172332-291287e9d060/go.mod h1:Ba5YVNCnyX6xDtg1JqEHa2EMVMW5UbHmIyEqsEwpeGE=
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg= github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/image/v5 v5.13.2 h1:AgYunV/9d2fRkrmo23wH2MkqeHolFd6oQCkK+1PpuFA= github.com/containers/image/v5 v5.13.2 h1:AgYunV/9d2fRkrmo23wH2MkqeHolFd6oQCkK+1PpuFA=
@ -259,7 +259,6 @@ github.com/containers/psgo v1.5.2 h1:3aoozst/GIwsrr/5jnFy3FrJay98uujPCu9lTuSZ/Cw
github.com/containers/psgo v1.5.2/go.mod h1:2ubh0SsreMZjSXW1Hif58JrEcFudQyIy9EzPUWfawVU= github.com/containers/psgo v1.5.2/go.mod h1:2ubh0SsreMZjSXW1Hif58JrEcFudQyIy9EzPUWfawVU=
github.com/containers/storage v1.23.5/go.mod h1:ha26Q6ngehFNhf3AWoXldvAvwI4jFe3ETQAf/CeZPyM= github.com/containers/storage v1.23.5/go.mod h1:ha26Q6ngehFNhf3AWoXldvAvwI4jFe3ETQAf/CeZPyM=
github.com/containers/storage v1.32.2/go.mod h1:YIBxxjfXZTi04Ah49sh1uSGfmT1V89+I5i3deRobzQo= 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 h1:NqdFRewXO/PYPjgCAScoigZc5QUA21yapSEj6kqD8cw=
github.com/containers/storage v1.32.6/go.mod h1:mdB+b89p+jU8zpzLTVXA0gWMmIo0WrkfGMh1R8O2IQw= 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= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
@ -698,7 +697,6 @@ github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
github.com/onsi/ginkgo v1.11.0/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.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg= github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg=
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 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= 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= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
@ -709,7 +707,6 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 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.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
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 h1:ep6kpPVwmr/nTbklSx2nrLNSIO62DoYAhnPNIMhK8gI=
github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= 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= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=

View File

@ -176,6 +176,11 @@ func ImageToImageSummary(l *libimage.Image) (*entities.ImageSummary, error) {
} }
containerCount := len(containers) containerCount := len(containers)
isDangling, err := l.IsDangling(context.TODO())
if err != nil {
return nil, errors.Wrapf(err, "failed to check if image %s is dangling", l.ID())
}
is := entities.ImageSummary{ is := entities.ImageSummary{
ID: l.ID(), ID: l.ID(),
ParentId: imageData.Parent, ParentId: imageData.Parent,
@ -188,7 +193,7 @@ func ImageToImageSummary(l *libimage.Image) (*entities.ImageSummary, error) {
Labels: imageData.Labels, Labels: imageData.Labels,
Containers: containerCount, Containers: containerCount,
ReadOnly: l.IsReadOnly(), ReadOnly: l.IsReadOnly(),
Dangling: l.IsDangling(), Dangling: isDangling,
Names: l.Names(), Names: l.Names(),
Digest: string(imageData.Digest), Digest: string(imageData.Digest),
ConfigDigest: "", // TODO: libpod/image didn't set it but libimage should ConfigDigest: "", // TODO: libpod/image didn't set it but libimage should

View File

@ -30,12 +30,16 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions)
for j, d := range img.Digests() { for j, d := range img.Digests() {
digests[j] = string(d) digests[j] = string(d)
} }
isDangling, err := img.IsDangling(ctx)
if err != nil {
return nil, errors.Wrapf(err, "error checking if image %q is dangling", img.ID())
}
e := entities.ImageSummary{ e := entities.ImageSummary{
ID: img.ID(), ID: img.ID(),
// ConfigDigest: string(img.ConfigDigest), // ConfigDigest: string(img.ConfigDigest),
Created: img.Created().Unix(), Created: img.Created().Unix(),
Dangling: img.IsDangling(), Dangling: isDangling,
Digest: string(img.Digest()), Digest: string(img.Digest()),
RepoDigests: digests, RepoDigests: digests,
History: img.NamesHistory(), History: img.NamesHistory(),

View File

@ -16,6 +16,11 @@ LABEL RUN podman --version
RUN apk update RUN apk update
RUN apk add bash`, ALPINE) RUN apk add bash`, ALPINE)
var emptyPruneImage = `
FROM scratch
ENV test1=test1
ENV test2=test2`
var _ = Describe("Podman prune", func() { var _ = Describe("Podman prune", func() {
var ( var (
tempdir string tempdir string
@ -110,8 +115,12 @@ var _ = Describe("Podman prune", func() {
Expect(session).Should(Exit(0)) Expect(session).Should(Exit(0))
Expect(len(session.OutputToStringArray())).To(Equal(numImages)) Expect(len(session.OutputToStringArray())).To(Equal(numImages))
// Now build a new image with dangling intermediate images. // Now build an image and untag it. The (intermediate) images
// should be removed recursively during pruning.
podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true") podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true")
session = podmanTest.Podman([]string{"untag", "alpine_bash:latest"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
session = podmanTest.Podman([]string{"images", "-a"}) session = podmanTest.Podman([]string{"images", "-a"})
session.WaitWithDefaultTimeout() session.WaitWithDefaultTimeout()
@ -136,26 +145,33 @@ var _ = Describe("Podman prune", func() {
Expect(len(session.OutputToStringArray())).To(Equal(numImages - numPrunedImages)) Expect(len(session.OutputToStringArray())).To(Equal(numImages - numPrunedImages))
}) })
It("podman image prune skip cache images", func() { It("podman image prune - handle empty images", func() {
podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true") // As shown in #10832, empty images were not treated correctly
// in Podman.
podmanTest.BuildImage(emptyPruneImage, "empty:scratch", "true")
none := podmanTest.Podman([]string{"images", "-a"}) session := podmanTest.Podman([]string{"images", "-a"})
none.WaitWithDefaultTimeout() session.WaitWithDefaultTimeout()
Expect(none).Should(Exit(0)) Expect(session).Should(Exit(0))
hasNone, _ := none.GrepString("<none>") hasNone, _ := session.GrepString("<none>")
Expect(hasNone).To(BeTrue()) Expect(hasNone).To(BeTrue())
prune := podmanTest.Podman([]string{"image", "prune", "-f"}) // Nothing will be pruned.
prune.WaitWithDefaultTimeout() session = podmanTest.Podman([]string{"image", "prune", "-f"})
Expect(prune).Should(Exit(0)) session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
Expect(len(session.OutputToStringArray())).To(Equal(0))
after := podmanTest.Podman([]string{"images", "-a"}) // Now the image will be untagged, and its parent images will
after.WaitWithDefaultTimeout() // be removed recursively.
Expect(none).Should(Exit(0)) session = podmanTest.Podman([]string{"untag", "empty:scratch"})
// Check if all "dangling" images were pruned. session.WaitWithDefaultTimeout()
hasNoneAfter, _ := after.GrepString("<none>") Expect(session).Should(Exit(0))
Expect(hasNoneAfter).To(BeFalse())
Expect(len(after.OutputToStringArray()) > 1).To(BeTrue()) session = podmanTest.Podman([]string{"image", "prune", "-f"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
Expect(len(session.OutputToStringArray())).To(Equal(2))
}) })
It("podman image prune dangling images", func() { It("podman image prune dangling images", func() {

View File

@ -30,7 +30,7 @@ env:
UBUNTU_NAME: "ubuntu-2104" UBUNTU_NAME: "ubuntu-2104"
PRIOR_UBUNTU_NAME: "ubuntu-2010" PRIOR_UBUNTU_NAME: "ubuntu-2010"
IMAGE_SUFFIX: "c6534244118822912" IMAGE_SUFFIX: "c6248193773010944"
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}" FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}" PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}"
UBUNTU_CACHE_IMAGE_NAME: "ubuntu-${IMAGE_SUFFIX}" UBUNTU_CACHE_IMAGE_NAME: "ubuntu-${IMAGE_SUFFIX}"

View File

@ -602,12 +602,43 @@ func (b *Builder) userForRun(mountPoint string, userspec string) (specs.User, st
// userForRun() does, except for the case where we're passed a single numeric // userForRun() does, except for the case where we're passed a single numeric
// value, where we need to use that value for both the UID and the GID. // value, where we need to use that value for both the UID and the GID.
func (b *Builder) userForCopy(mountPoint string, userspec string) (uint32, uint32, error) { func (b *Builder) userForCopy(mountPoint string, userspec string) (uint32, uint32, error) {
if id, err := strconv.ParseUint(userspec, 10, 32); err == nil { var (
return uint32(id), uint32(id), nil user, group string
uid, gid uint64
err error
)
split := strings.SplitN(userspec, ":", 2)
user = split[0]
if len(split) > 1 {
group = split[1]
} }
user, _, err := b.userForRun(mountPoint, userspec)
// If userspec did not specify any values for user or group, then fail
if user == "" && group == "" {
return 0, 0, errors.Errorf("can't find uid for user %s", userspec)
}
// If userspec specifies values for user or group, check for numeric values
// and return early. If not, then translate username/groupname
if user != "" {
uid, err = strconv.ParseUint(user, 10, 32)
}
if err == nil {
// default gid to uid
gid = uid
if group != "" {
gid, err = strconv.ParseUint(group, 10, 32)
}
}
// If err != nil, then user or group not numeric, check filesystem
if err == nil {
return uint32(uid), uint32(gid), nil
}
owner, _, err := b.userForRun(mountPoint, userspec)
if err != nil { if err != nil {
return 0xffffffff, 0xffffffff, err return 0xffffffff, 0xffffffff, err
} }
return user.UID, user.GID, nil return owner.UID, owner.GID, nil
} }

View File

@ -161,7 +161,7 @@ func RunUsingChroot(spec *specs.Spec, bundlePath, homeDir string, stdin io.Reade
cmd := unshare.Command(runUsingChrootCommand) cmd := unshare.Command(runUsingChrootCommand)
cmd.Stdin, cmd.Stdout, cmd.Stderr = stdin, stdout, stderr cmd.Stdin, cmd.Stdout, cmd.Stderr = stdin, stdout, stderr
cmd.Dir = "/" cmd.Dir = "/"
cmd.Env = append([]string{fmt.Sprintf("LOGLEVEL=%d", logrus.GetLevel())}, os.Environ()...) cmd.Env = []string{fmt.Sprintf("LOGLEVEL=%d", logrus.GetLevel())}
logrus.Debugf("Running %#v in %#v", cmd.Cmd, cmd) logrus.Debugf("Running %#v in %#v", cmd.Cmd, cmd)
confwg.Add(1) confwg.Add(1)
@ -207,7 +207,7 @@ func runUsingChrootMain() {
os.Exit(1) os.Exit(1)
} }
if options.Spec == nil { if options.Spec == nil || options.Spec.Process == nil {
fmt.Fprintf(os.Stderr, "invalid options spec in runUsingChrootMain\n") fmt.Fprintf(os.Stderr, "invalid options spec in runUsingChrootMain\n")
os.Exit(1) os.Exit(1)
} }
@ -573,7 +573,7 @@ func runUsingChroot(spec *specs.Spec, bundlePath string, ctty *os.File, stdin io
cmd := unshare.Command(append([]string{runUsingChrootExecCommand}, spec.Process.Args...)...) cmd := unshare.Command(append([]string{runUsingChrootExecCommand}, spec.Process.Args...)...)
cmd.Stdin, cmd.Stdout, cmd.Stderr = stdin, stdout, stderr cmd.Stdin, cmd.Stdout, cmd.Stderr = stdin, stdout, stderr
cmd.Dir = "/" cmd.Dir = "/"
cmd.Env = append([]string{fmt.Sprintf("LOGLEVEL=%d", logrus.GetLevel())}, os.Environ()...) cmd.Env = []string{fmt.Sprintf("LOGLEVEL=%d", logrus.GetLevel())}
cmd.UnshareFlags = syscall.CLONE_NEWUTS | syscall.CLONE_NEWNS cmd.UnshareFlags = syscall.CLONE_NEWUTS | syscall.CLONE_NEWNS
requestedUserNS := false requestedUserNS := false
for _, ns := range spec.Linux.Namespaces { for _, ns := range spec.Linux.Namespaces {
@ -663,7 +663,7 @@ func runUsingChrootExecMain() {
// Set the hostname. We're already in a distinct UTS namespace and are admins in the user // Set the hostname. We're already in a distinct UTS namespace and are admins in the user
// namespace which created it, so we shouldn't get a permissions error, but seccomp policy // namespace which created it, so we shouldn't get a permissions error, but seccomp policy
// might deny our attempt to call sethostname() anyway, so log a debug message for that. // might deny our attempt to call sethostname() anyway, so log a debug message for that.
if options.Spec == nil { if options.Spec == nil || options.Spec.Process == nil {
fmt.Fprintf(os.Stderr, "invalid options spec passed in\n") fmt.Fprintf(os.Stderr, "invalid options spec passed in\n")
os.Exit(1) os.Exit(1)
} }
@ -819,7 +819,6 @@ func runUsingChrootExecMain() {
// Output debug messages when that differs from what we're being asked to do. // Output debug messages when that differs from what we're being asked to do.
func logNamespaceDiagnostics(spec *specs.Spec) { func logNamespaceDiagnostics(spec *specs.Spec) {
sawMountNS := false sawMountNS := false
sawUserNS := false
sawUTSNS := false sawUTSNS := false
for _, ns := range spec.Linux.Namespaces { for _, ns := range spec.Linux.Namespaces {
switch ns.Type { switch ns.Type {
@ -854,9 +853,8 @@ func logNamespaceDiagnostics(spec *specs.Spec) {
} }
case specs.UserNamespace: case specs.UserNamespace:
if ns.Path != "" { if ns.Path != "" {
logrus.Debugf("unable to join user namespace %q, creating a new one", ns.Path) logrus.Debugf("unable to join user namespace, sorry about that")
} }
sawUserNS = true
case specs.UTSNamespace: case specs.UTSNamespace:
if ns.Path != "" { if ns.Path != "" {
logrus.Debugf("unable to join UTS namespace %q, creating a new one", ns.Path) logrus.Debugf("unable to join UTS namespace %q, creating a new one", ns.Path)
@ -867,9 +865,6 @@ func logNamespaceDiagnostics(spec *specs.Spec) {
if !sawMountNS { if !sawMountNS {
logrus.Debugf("mount namespace not requested, but creating a new one anyway") logrus.Debugf("mount namespace not requested, but creating a new one anyway")
} }
if !sawUserNS {
logrus.Debugf("user namespace not requested, but creating a new one anyway")
}
if !sawUTSNS { if !sawUTSNS {
logrus.Debugf("UTS namespace not requested, but creating a new one anyway") logrus.Debugf("UTS namespace not requested, but creating a new one anyway")
} }

View File

@ -4,10 +4,10 @@ go 1.12
require ( require (
github.com/containernetworking/cni v0.8.1 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/image/v5 v5.13.2
github.com/containers/ocicrypt v1.1.2 github.com/containers/ocicrypt v1.1.2
github.com/containers/storage v1.32.5 github.com/containers/storage v1.32.6
github.com/docker/distribution v2.7.1+incompatible github.com/docker/distribution v2.7.1+incompatible
github.com/docker/go-units v0.4.0 github.com/docker/go-units v0.4.0
github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316 github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316
@ -18,10 +18,10 @@ require (
github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect
github.com/mattn/go-shellwords v1.0.12 github.com/mattn/go-shellwords v1.0.12
github.com/onsi/ginkgo v1.16.4 github.com/onsi/ginkgo v1.16.4
github.com/onsi/gomega v1.13.0 github.com/onsi/gomega v1.14.0
github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 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-spec v1.0.3-0.20210326190908-1c3f411f0417
github.com/opencontainers/runtime-tools v0.9.0 github.com/opencontainers/runtime-tools v0.9.0
github.com/opencontainers/selinux v1.8.2 github.com/opencontainers/selinux v1.8.2

View File

@ -73,8 +73,9 @@ github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg3
github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00=
github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600=
github.com/Microsoft/hcsshim v0.8.17 h1:yFHH5bghP9ij5Y34PPaMOE8g//oXZ0uJQeMENVo2zcI=
github.com/Microsoft/hcsshim v0.8.17/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= github.com/Microsoft/hcsshim v0.8.17/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4=
github.com/Microsoft/hcsshim v0.8.20 h1:ZTwcx3NS8n07kPf/JZ1qwU6vnjhVPMUWlXBF8r9UxrE=
github.com/Microsoft/hcsshim v0.8.20/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4=
github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU=
github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
@ -136,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.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
github.com/cilium/ebpf v0.5.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.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/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-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@ -223,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/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.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM=
github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= 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.41.1-0.20210721112610-c95d2f794edf h1:z0ciG0ByyJG3WCBpLYd2XLThCC7UBaH7GeSfXY4sAqc=
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/go.mod h1:Ba5YVNCnyX6xDtg1JqEHa2EMVMW5UbHmIyEqsEwpeGE=
github.com/containers/image/v5 v5.13.2 h1:AgYunV/9d2fRkrmo23wH2MkqeHolFd6oQCkK+1PpuFA= 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/image/v5 v5.13.2/go.mod h1:GkWursKDlDcUIT7L7vZf70tADvZCk/Ga0wgS0MuF0ag=
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE=
@ -235,8 +237,8 @@ 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 h1:Ez+GAMP/4GLix5Ywo/fL7O0nY771gsBIigiqUm1aXz0=
github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= 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.2/go.mod h1:YIBxxjfXZTi04Ah49sh1uSGfmT1V89+I5i3deRobzQo=
github.com/containers/storage v1.32.5 h1:DXgmyA+oOs7YAzKkEqgC5O8l2UuDGJcwEFbdt49qiak= github.com/containers/storage v1.32.6 h1:NqdFRewXO/PYPjgCAScoigZc5QUA21yapSEj6kqD8cw=
github.com/containers/storage v1.32.5/go.mod h1:8/DVVDqniaUlUV0D0q7cEnXK6Bs2uU3FPqNZVPumwEs= 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= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
@ -609,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.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.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.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 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= 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= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
@ -618,8 +619,8 @@ 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.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.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= github.com/onsi/gomega v1.14.0 h1:ep6kpPVwmr/nTbklSx2nrLNSIO62DoYAhnPNIMhK8gI=
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= 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= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
@ -636,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-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-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-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.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 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.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= github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=

View File

@ -39,7 +39,7 @@ const (
) )
// Mount is a mountpoint for the build container. // Mount is a mountpoint for the build container.
type Mount specs.Mount type Mount = specs.Mount
type BuildOptions = define.BuildOptions type BuildOptions = define.BuildOptions

View File

@ -165,8 +165,7 @@ func NewExecutor(logger *logrus.Logger, store storage.Store, options define.Buil
if err != nil { if err != nil {
return nil, err return nil, err
} }
transientMounts = append([]Mount{mount}, transientMounts...)
transientMounts = append([]Mount{Mount(mount)}, transientMounts...)
} }
secrets, err := parse.Secrets(options.CommonBuildOpts.Secrets) secrets, err := parse.Secrets(options.CommonBuildOpts.Secrets)
@ -569,7 +568,7 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image
// Build maps of every named base image and every referenced stage root // Build maps of every named base image and every referenced stage root
// filesystem. Individual stages can use them to determine whether or // filesystem. Individual stages can use them to determine whether or
// not they can skip certain steps near the end of their stages. // not they can skip certain steps near the end of their stages.
for _, stage := range stages { for stageIndex, stage := range stages {
node := stage.Node // first line node := stage.Node // first line
for node != nil { // each line for node != nil { // each line
for _, child := range node.Children { // tokens on this line, though we only care about the first for _, child := range node.Children { // tokens on this line, though we only care about the first
@ -589,7 +588,7 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image
// FROM instruction uses argument values, // FROM instruction uses argument values,
// we might not record the right value here. // we might not record the right value here.
b.baseMap[base] = true b.baseMap[base] = true
logrus.Debugf("base: %q", base) logrus.Debugf("base for stage %d: %q", stageIndex, base)
} }
} }
case "ADD", "COPY": case "ADD", "COPY":
@ -601,7 +600,7 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image
// not record the right value here. // not record the right value here.
rootfs := strings.TrimPrefix(flag, "--from=") rootfs := strings.TrimPrefix(flag, "--from=")
b.rootfsMap[rootfs] = true b.rootfsMap[rootfs] = true
logrus.Debugf("rootfs: %q", rootfs) logrus.Debugf("rootfs needed for COPY in stage %d: %q", stageIndex, rootfs)
} }
} }
} }

View File

@ -434,7 +434,7 @@ func (s *StageExecutor) Run(run imagebuilder.Run, config docker.Config) error {
Runtime: s.executor.runtime, Runtime: s.executor.runtime,
Args: s.executor.runtimeArgs, Args: s.executor.runtimeArgs,
NoPivot: os.Getenv("BUILDAH_NOPIVOT") != "", NoPivot: os.Getenv("BUILDAH_NOPIVOT") != "",
Mounts: convertMounts(s.executor.transientMounts), Mounts: append([]Mount{}, s.executor.transientMounts...),
Env: config.Env, Env: config.Env,
User: config.User, User: config.User,
WorkingDir: config.WorkingDir, WorkingDir: config.WorkingDir,
@ -557,13 +557,6 @@ func (s *StageExecutor) prepare(ctx context.Context, from string, initializeIBCo
OciDecryptConfig: s.executor.ociDecryptConfig, OciDecryptConfig: s.executor.ociDecryptConfig,
} }
// Check and see if the image is a pseudonym for the end result of a
// previous stage, named by an AS clause in the Dockerfile.
s.executor.stagesLock.Lock()
if asImageFound, ok := s.executor.imageMap[from]; ok {
builderOptions.FromImage = asImageFound
}
s.executor.stagesLock.Unlock()
builder, err = buildah.NewBuilder(ctx, s.executor.store, builderOptions) builder, err = buildah.NewBuilder(ctx, s.executor.store, builderOptions)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "error creating build container") return nil, errors.Wrapf(err, "error creating build container")
@ -684,15 +677,20 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
// If the base image's name corresponds to the result of an earlier // If the base image's name corresponds to the result of an earlier
// stage, make sure that stage has finished building an image, and // stage, make sure that stage has finished building an image, and
// substitute that image's ID for the base image's name here. If not, // substitute that image's ID for the base image's name here and force
// then go on assuming that it's just a regular image that's either in // the pull policy to "never" to avoid triggering an error when it's
// local storage, or one that we have to pull from a registry. // set to "always", which doesn't make sense for image IDs.
// If not, then go on assuming that it's just a regular image that's
// either in local storage, or one that we have to pull from a
// registry, subject to the passed-in pull policy.
if isStage, err := s.executor.waitForStage(ctx, base, s.stages[:s.index]); isStage && err != nil { if isStage, err := s.executor.waitForStage(ctx, base, s.stages[:s.index]); isStage && err != nil {
return "", nil, err return "", nil, err
} }
pullPolicy := s.executor.pullPolicy
s.executor.stagesLock.Lock() s.executor.stagesLock.Lock()
if stageImage, isPreviousStage := s.executor.imageMap[base]; isPreviousStage { if stageImage, isPreviousStage := s.executor.imageMap[base]; isPreviousStage {
base = stageImage base = stageImage
pullPolicy = define.PullNever
} }
s.executor.stagesLock.Unlock() s.executor.stagesLock.Unlock()
@ -723,7 +721,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
// Create the (first) working container for this stage. Reinitializing // Create the (first) working container for this stage. Reinitializing
// the imagebuilder configuration may alter the list of steps we have, // the imagebuilder configuration may alter the list of steps we have,
// so take a snapshot of them *after* that. // so take a snapshot of them *after* that.
if _, err := s.prepare(ctx, base, true, true, s.executor.pullPolicy); err != nil { if _, err := s.prepare(ctx, base, true, true, pullPolicy); err != nil {
return "", nil, err return "", nil, err
} }
children := stage.Node.Children children := stage.Node.Children

View File

@ -2,26 +2,11 @@ package imagebuildah
import ( import (
"github.com/containers/buildah" "github.com/containers/buildah"
"github.com/opencontainers/runtime-spec/specs-go"
) )
// InitReexec is a wrapper for buildah.InitReexec(). It should be called at // InitReexec is a wrapper for buildah.InitReexec(). It should be called at
// the start of main(), and if it returns true, main() should return // the start of main(), and if it returns true, main() should return
// immediately. // successfully immediately.
func InitReexec() bool { func InitReexec() bool {
return buildah.InitReexec() return buildah.InitReexec()
} }
func convertMounts(mounts []Mount) []specs.Mount {
specmounts := []specs.Mount{}
for _, m := range mounts {
s := specs.Mount{
Destination: m.Destination,
Type: m.Type,
Source: m.Source,
Options: m.Options,
}
specmounts = append(specmounts, s)
}
return specmounts
}

View File

@ -153,9 +153,7 @@ sudo apt-get -qq -y install buildah
### Kernel Version Requirements ### Kernel Version Requirements
To run Buildah on Red Hat Enterprise Linux or CentOS, version 7.4 or higher is required. To run Buildah on Red Hat Enterprise Linux or CentOS, version 7.4 or higher is required.
On other Linux distributions Buildah requires a kernel version of 4.0 or On other Linux distributions Buildah requires a kernel version that supports the OverlayFS and/or fuse-overlayfs filesystem -- you'll need to consult your distribution's documentation to determine a minimum version number.
higher in order to support the OverlayFS filesystem. The kernel version can be checked
with the 'uname -a' command.
### runc Requirement ### runc Requirement

View File

@ -15,6 +15,7 @@ import (
"unicode" "unicode"
"github.com/containers/buildah/define" "github.com/containers/buildah/define"
"github.com/containers/common/pkg/parse"
"github.com/containers/image/v5/types" "github.com/containers/image/v5/types"
"github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/unshare" "github.com/containers/storage/pkg/unshare"
@ -205,13 +206,13 @@ func Volume(volume string) (specs.Mount, error) {
if err := validateVolumeMountHostDir(arr[0]); err != nil { if err := validateVolumeMountHostDir(arr[0]); err != nil {
return mount, err return mount, err
} }
if err := ValidateVolumeCtrDir(arr[1]); err != nil { if err := parse.ValidateVolumeCtrDir(arr[1]); err != nil {
return mount, err return mount, err
} }
mountOptions := "" mountOptions := ""
if len(arr) > 2 { if len(arr) > 2 {
mountOptions = arr[2] mountOptions = arr[2]
if _, err := ValidateVolumeOpts(strings.Split(arr[2], ",")); err != nil { if _, err := parse.ValidateVolumeOpts(strings.Split(arr[2], ",")); err != nil {
return mount, err return mount, err
} }
} }
@ -360,7 +361,7 @@ func GetBindMount(args []string) (specs.Mount, error) {
if len(kv) == 1 { if len(kv) == 1 {
return newMount, errors.Wrapf(optionArgError, kv[0]) return newMount, errors.Wrapf(optionArgError, kv[0])
} }
if err := ValidateVolumeHostDir(kv[1]); err != nil { if err := parse.ValidateVolumeHostDir(kv[1]); err != nil {
return newMount, err return newMount, err
} }
newMount.Source = kv[1] newMount.Source = kv[1]
@ -369,7 +370,7 @@ func GetBindMount(args []string) (specs.Mount, error) {
if len(kv) == 1 { if len(kv) == 1 {
return newMount, errors.Wrapf(optionArgError, kv[0]) return newMount, errors.Wrapf(optionArgError, kv[0])
} }
if err := ValidateVolumeCtrDir(kv[1]); err != nil { if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
return newMount, err return newMount, err
} }
newMount.Destination = kv[1] newMount.Destination = kv[1]
@ -391,7 +392,7 @@ func GetBindMount(args []string) (specs.Mount, error) {
newMount.Source = newMount.Destination newMount.Source = newMount.Destination
} }
opts, err := ValidateVolumeOpts(newMount.Options) opts, err := parse.ValidateVolumeOpts(newMount.Options)
if err != nil { if err != nil {
return newMount, err return newMount, err
} }
@ -433,7 +434,7 @@ func GetTmpfsMount(args []string) (specs.Mount, error) {
if len(kv) == 1 { if len(kv) == 1 {
return newMount, errors.Wrapf(optionArgError, kv[0]) return newMount, errors.Wrapf(optionArgError, kv[0])
} }
if err := ValidateVolumeCtrDir(kv[1]); err != nil { if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
return newMount, err return newMount, err
} }
newMount.Destination = kv[1] newMount.Destination = kv[1]
@ -452,17 +453,7 @@ func GetTmpfsMount(args []string) (specs.Mount, error) {
// ValidateVolumeHostDir validates a volume mount's source directory // ValidateVolumeHostDir validates a volume mount's source directory
func ValidateVolumeHostDir(hostDir string) error { func ValidateVolumeHostDir(hostDir string) error {
if len(hostDir) == 0 { return parse.ValidateVolumeHostDir(hostDir)
return errors.Errorf("host directory cannot be empty")
}
if filepath.IsAbs(hostDir) {
if _, err := os.Stat(hostDir); err != nil {
return errors.WithStack(err)
}
}
// If hostDir is not an absolute path, that means the user wants to create a
// named volume. This will be done later on in the code.
return nil
} }
// validates the host path of buildah --volume // validates the host path of buildah --volume
@ -478,75 +469,12 @@ func validateVolumeMountHostDir(hostDir string) error {
// ValidateVolumeCtrDir validates a volume mount's destination directory. // ValidateVolumeCtrDir validates a volume mount's destination directory.
func ValidateVolumeCtrDir(ctrDir string) error { func ValidateVolumeCtrDir(ctrDir string) error {
if len(ctrDir) == 0 { return parse.ValidateVolumeCtrDir(ctrDir)
return errors.Errorf("container directory cannot be empty")
}
if !filepath.IsAbs(ctrDir) {
return errors.Errorf("invalid container path %q, must be an absolute path", ctrDir)
}
return nil
} }
// ValidateVolumeOpts validates a volume's options // ValidateVolumeOpts validates a volume's options
func ValidateVolumeOpts(options []string) ([]string, error) { func ValidateVolumeOpts(options []string) ([]string, error) {
var foundRootPropagation, foundRWRO, foundLabelChange, bindType, foundExec, foundDev, foundSuid, foundChown int return parse.ValidateVolumeOpts(options)
finalOpts := make([]string, 0, len(options))
for _, opt := range options {
switch opt {
case "noexec", "exec":
foundExec++
if foundExec > 1 {
return nil, errors.Errorf("invalid options %q, can only specify 1 'noexec' or 'exec' option", strings.Join(options, ", "))
}
case "nodev", "dev":
foundDev++
if foundDev > 1 {
return nil, errors.Errorf("invalid options %q, can only specify 1 'nodev' or 'dev' option", strings.Join(options, ", "))
}
case "nosuid", "suid":
foundSuid++
if foundSuid > 1 {
return nil, errors.Errorf("invalid options %q, can only specify 1 'nosuid' or 'suid' option", strings.Join(options, ", "))
}
case "rw", "ro":
foundRWRO++
if foundRWRO > 1 {
return nil, errors.Errorf("invalid options %q, can only specify 1 'rw' or 'ro' option", strings.Join(options, ", "))
}
case "z", "Z", "O":
foundLabelChange++
if foundLabelChange > 1 {
return nil, errors.Errorf("invalid options %q, can only specify 1 'z', 'Z', or 'O' option", strings.Join(options, ", "))
}
case "U":
foundChown++
if foundChown > 1 {
return nil, errors.Errorf("invalid options %q, can only specify 1 'U' option", strings.Join(options, ", "))
}
case "private", "rprivate", "shared", "rshared", "slave", "rslave", "unbindable", "runbindable":
foundRootPropagation++
if foundRootPropagation > 1 {
return nil, errors.Errorf("invalid options %q, can only specify 1 '[r]shared', '[r]private', '[r]slave' or '[r]unbindable' option", strings.Join(options, ", "))
}
case "bind", "rbind":
bindType++
if bindType > 1 {
return nil, errors.Errorf("invalid options %q, can only specify 1 '[r]bind' option", strings.Join(options, ", "))
}
case "cached", "delegated":
// The discarded ops are OS X specific volume options
// introduced in a recent Docker version.
// They have no meaning on Linux, so here we silently
// drop them. This matches Docker's behavior (the options
// are intended to be always safe to use, even not on OS
// X).
continue
default:
return nil, errors.Errorf("invalid option type %q", opt)
}
finalOpts = append(finalOpts, opt)
}
return finalOpts, nil
} }
// validateExtraHost validates that the specified string is a valid extrahost and returns it. // validateExtraHost validates that the specified string is a valid extrahost and returns it.
@ -601,7 +529,7 @@ func SystemContextFromOptions(c *cobra.Command) (*types.SystemContext, error) {
creds, err := c.Flags().GetString("creds") creds, err := c.Flags().GetString("creds")
if err == nil && c.Flag("creds").Changed { if err == nil && c.Flag("creds").Changed {
var err error var err error
ctx.DockerAuthConfig, err = getDockerAuth(creds) ctx.DockerAuthConfig, err = AuthConfig(creds)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -734,7 +662,9 @@ func parseCreds(creds string) (string, string) {
return up[0], up[1] return up[0], up[1]
} }
func getDockerAuth(creds string) (*types.DockerAuthConfig, error) { // AuthConfig parses the creds in format [username[:password] into an auth
// config.
func AuthConfig(creds string) (*types.DockerAuthConfig, error) {
username, password := parseCreds(creds) username, password := parseCreds(creds)
if username == "" { if username == "" {
fmt.Print("Username: ") fmt.Print("Username: ")

View File

@ -88,7 +88,7 @@ func (r *Runtime) compileImageFilters(ctx context.Context, filters []string) ([]
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "non-boolean value %q for dangling filter", value) return nil, errors.Wrapf(err, "non-boolean value %q for dangling filter", value)
} }
filterFuncs = append(filterFuncs, filterDangling(dangling)) filterFuncs = append(filterFuncs, filterDangling(ctx, dangling))
case "id": case "id":
filterFuncs = append(filterFuncs, filterID(value)) filterFuncs = append(filterFuncs, filterID(value))
@ -201,9 +201,13 @@ func filterContainers(value bool) filterFunc {
} }
// filterDangling creates a dangling filter for matching the specified value. // filterDangling creates a dangling filter for matching the specified value.
func filterDangling(value bool) filterFunc { func filterDangling(ctx context.Context, value bool) filterFunc {
return func(img *Image) (bool, error) { return func(img *Image) (bool, error) {
return img.IsDangling() == value, nil isDangling, err := img.IsDangling(ctx)
if err != nil {
return false, err
}
return isDangling == value, nil
} }
} }

View File

@ -121,24 +121,29 @@ func (i *Image) IsReadOnly() bool {
return i.storageImage.ReadOnly return i.storageImage.ReadOnly
} }
// IsDangling returns true if the image is dangling. An image is considered // IsDangling returns true if the image is dangling, that is an untagged image
// dangling if no names are associated with it in the containers storage. // without children.
func (i *Image) IsDangling() bool { func (i *Image) IsDangling(ctx context.Context) (bool, error) {
return len(i.Names()) == 0 if len(i.Names()) > 0 {
} return false, nil
}
// IsIntermediate returns true if the image is an intermediate image, that is children, err := i.getChildren(ctx, false)
// a dangling image without children. if err != nil {
func (i *Image) IsIntermediate(ctx context.Context) (bool, error) { return false, err
// If the image has tags, it's not an intermediate one. }
if !i.IsDangling() { return len(children) == 0, nil
}
// IsIntermediate returns true if the image is an intermediate image, that is
// an untagged image with children.
func (i *Image) IsIntermediate(ctx context.Context) (bool, error) {
if len(i.Names()) > 0 {
return false, nil return false, nil
} }
children, err := i.getChildren(ctx, false) children, err := i.getChildren(ctx, false)
if err != nil { if err != nil {
return false, err return false, err
} }
// No tags, no children -> intermediate!
return len(children) != 0, nil 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 // 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 // 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. // If the image is used by containers return storage.ErrImageUsedByContainer.
// Use force to remove these containers. // Use force to remove these containers.
@ -282,7 +287,12 @@ type RemoveImageReport struct {
// //
// This function is internal. Users of libimage should always use // This function is internal. Users of libimage should always use
// `(*Runtime).RemoveImages()`. // `(*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 // If referencedBy is empty, the image is considered to be removed via
// `image remove --all` which alters the logic below. // `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()) logrus.Debugf("Removing image %s", i.ID())
if i.IsReadOnly() { 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 { if i.runtime.eventChannel != nil {
@ -306,7 +316,7 @@ func (i *Image) remove(ctx context.Context, rmMap map[string]*RemoveImageReport,
if exists { if exists {
// If the image has already been removed, we're done. // If the image has already been removed, we're done.
if report.Removed { if report.Removed {
return nil return processedIDs, nil
} }
} else { } else {
report = &RemoveImageReport{ID: i.ID()} report = &RemoveImageReport{ID: i.ID()}
@ -333,7 +343,7 @@ func (i *Image) remove(ctx context.Context, rmMap map[string]*RemoveImageReport,
if options.WithSize { if options.WithSize {
size, err := i.Size() size, err := i.Size()
if handleError(err) != nil { if handleError(err) != nil {
return err return processedIDs, err
} }
report.Size = size report.Size = size
} }
@ -354,18 +364,18 @@ func (i *Image) remove(ctx context.Context, rmMap map[string]*RemoveImageReport,
byDigest := strings.HasPrefix(referencedBy, "sha256:") byDigest := strings.HasPrefix(referencedBy, "sha256:")
if !options.Force { if !options.Force {
if byID && numNames > 1 { 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 { } else if byDigest && numNames > 1 {
// FIXME - Docker will remove the digest but containers storage // FIXME - Docker will remove the digest but containers storage
// does not support that yet, so our hands are tied. // 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. // Only try to untag if we know it's not an ID or digest.
if !byID && !byDigest { if !byID && !byDigest {
if err := i.Untag(referencedBy); handleError(err) != nil { if err := i.Untag(referencedBy); handleError(err) != nil {
return err return processedIDs, err
} }
report.Untagged = append(report.Untagged, referencedBy) 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 { if skipRemove {
return nil return processedIDs, nil
} }
// Perform the actual removal. First, remove containers if needed. // Perform the actual removal. First, remove containers if needed.
if options.Force { if options.Force {
if err := i.removeContainers(options.RemoveContainerFunc); err != nil { 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 { if _, err := i.runtime.store.DeleteImage(i.ID(), true); handleError(err) != nil {
return err return processedIDs, err
} }
report.Untagged = append(report.Untagged, i.Names()...) 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. // Check if can remove the parent image.
if parent == nil { if parent == nil {
return nil return processedIDs, nil
} }
if !parent.IsDangling() { // Only remove the parent if it's dangling, that is being untagged and
return nil // without children.
} danglingParent, err := parent.IsDangling(ctx)
// If the image has siblings, we don't remove the parent.
hasSiblings, err := parent.HasChildren(ctx)
if err != nil { if err != nil {
// See Podman commit fd9dd7065d44: we need to // See Podman commit fd9dd7065d44: we need to
// be tolerant toward corrupted images. // be tolerant toward corrupted images.
logrus.Warnf("error determining if an image is a parent: %v, ignoring the error", err) logrus.Warnf("error determining if an image is a parent: %v, ignoring the error", err)
hasSiblings = false danglingParent = false
} }
if hasSiblings { if !danglingParent {
return nil return processedIDs, nil
} }
// Recurse into removing the parent. // 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 // Tag the image with the specified name and store it in the local containers

View File

@ -15,6 +15,9 @@ type layerTree struct {
// ociCache is a cache for Image.ID -> OCI Image. Translations are done // ociCache is a cache for Image.ID -> OCI Image. Translations are done
// on-demand. // on-demand.
ociCache map[string]*ociv1.Image 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. // 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 img := images[i] // do not leak loop variable outside the scope
topLayer := img.TopLayer() topLayer := img.TopLayer()
if topLayer == "" { if topLayer == "" {
tree.emptyImages = append(tree.emptyImages, img)
continue continue
} }
node, exists := tree.nodes[topLayer] 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. // 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 // 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 // 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) { func (t *layerTree) children(ctx context.Context, parent *Image, all bool) ([]*Image, error) {
if parent.TopLayer() == "" { if parent.TopLayer() == "" {
if isManifestList, _ := parent.IsManifestList(ctx); isManifestList {
return nil, nil 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
} }
parentID := parent.ID() parentID := parent.ID()
@ -163,6 +158,38 @@ func (t *layerTree) children(ctx context.Context, parent *Image, all bool) ([]*I
return areParentAndChild(parentOCI, childOCI), nil 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 // addChildrenFrom adds child images of parent to children. Returns
// true if any image is a child of parent. // true if any image is a child of parent.
addChildrenFromNode := func(node *layerNode) (bool, error) { 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. // 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) { func (t *layerTree) parent(ctx context.Context, child *Image) (*Image, error) {
if child.TopLayer() == "" { 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 return nil, nil
} }
@ -219,14 +275,8 @@ func (t *layerTree) parent(ctx context.Context, child *Image) (*Image, error) {
return nil, nil 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 // Check images from the parent node (i.e., parent layer) and images
// with the same layer (i.e., same top layer). // with the same layer (i.e., same top layer).
childID := child.ID()
images := node.images images := node.images
if node.parent != nil { if node.parent != nil {
images = append(images, node.parent.images...) images = append(images, node.parent.images...)

View File

@ -10,6 +10,7 @@ import (
"github.com/containers/common/pkg/config" "github.com/containers/common/pkg/config"
registryTransport "github.com/containers/image/v5/docker" registryTransport "github.com/containers/image/v5/docker"
dockerArchiveTransport "github.com/containers/image/v5/docker/archive" dockerArchiveTransport "github.com/containers/image/v5/docker/archive"
dockerDaemonTransport "github.com/containers/image/v5/docker/daemon"
"github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/docker/reference"
ociArchiveTransport "github.com/containers/image/v5/oci/archive" ociArchiveTransport "github.com/containers/image/v5/oci/archive"
ociTransport "github.com/containers/image/v5/oci/layout" ociTransport "github.com/containers/image/v5/oci/layout"
@ -55,6 +56,13 @@ func (r *Runtime) Pull(ctx context.Context, name string, pullPolicy config.PullP
var possiblyUnqualifiedName string // used for short-name resolution var possiblyUnqualifiedName string // used for short-name resolution
ref, err := alltransports.ParseImageName(name) ref, err := alltransports.ParseImageName(name)
if err != nil { if err != nil {
// Check whether `name` points to a transport. If so, we
// return the error. Otherwise we assume that `name` refers to
// an image on a registry (e.g., "fedora").
if alltransports.TransportFromImageName(name) != nil {
return nil, err
}
// If the image clearly refers to a local one, we can look it up directly. // If the image clearly refers to a local one, we can look it up directly.
// In fact, we need to since they are not parseable. // In fact, we need to since they are not parseable.
if strings.HasPrefix(name, "sha256:") || (len(name) == 64 && !strings.ContainsAny(name, "/.:@")) { if strings.HasPrefix(name, "sha256:") || (len(name) == 64 && !strings.ContainsAny(name, "/.:@")) {
@ -169,6 +177,15 @@ func (r *Runtime) copyFromDefault(ctx context.Context, ref types.ImageReference,
var storageName, imageName string var storageName, imageName string
switch ref.Transport().Name() { switch ref.Transport().Name() {
case dockerDaemonTransport.Transport.Name():
// Normalize to docker.io if needed (see containers/podman/issues/10998).
named, err := reference.ParseNormalizedNamed(ref.StringWithinTransport())
if err != nil {
return nil, err
}
imageName = named.String()
storageName = imageName
case ociTransport.Transport.Name(): case ociTransport.Transport.Name():
split := strings.SplitN(ref.StringWithinTransport(), ":", 2) split := strings.SplitN(ref.StringWithinTransport(), ":", 2)
storageName = toLocalImageName(split[0]) storageName = toLocalImageName(split[0])

View File

@ -3,7 +3,6 @@ package libimage
import ( import (
"context" "context"
"os" "os"
"path/filepath"
"strings" "strings"
"github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/docker/reference"
@ -94,10 +93,6 @@ func RuntimeFromStore(store storage.Store, options *RuntimeOptions) (*Runtime, e
setRegistriesConfPath(&systemContext) setRegistriesConfPath(&systemContext)
if systemContext.BlobInfoCacheDir == "" {
systemContext.BlobInfoCacheDir = filepath.Join(store.GraphRoot(), "cache")
}
return &Runtime{ return &Runtime{
store: store, store: store,
systemContext: systemContext, systemContext: systemContext,
@ -592,11 +587,10 @@ func (r *Runtime) RemoveImages(ctx context.Context, names []string, options *Rem
rmErrors = append(rmErrors, err) rmErrors = append(rmErrors, err)
} }
orderedIDs := []string{} // determinism and relative order
deleteMap := make(map[string]*deleteMe) // ID -> deleteMe deleteMap := make(map[string]*deleteMe) // ID -> deleteMe
toDelete := []string{}
// Look up images in the local containers storage and fill out // Look up images in the local containers storage and fill out
// orderedIDs and the deleteMap. // toDelete and the deleteMap.
switch { switch {
case len(names) > 0: case len(names) > 0:
// Look up the images one-by-one. That allows for removing // 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()] dm, exists := deleteMap[img.ID()]
if !exists { if !exists {
orderedIDs = append(orderedIDs, img.ID()) toDelete = append(toDelete, img.ID())
dm = &deleteMe{image: img} dm = &deleteMe{image: img}
deleteMap[img.ID()] = dm deleteMap[img.ID()] = dm
} }
dm.referencedBy = append(dm.referencedBy, resolvedName) dm.referencedBy = append(dm.referencedBy, resolvedName)
} }
if len(orderedIDs) == 0 {
return nil, rmErrors
}
default: default:
filteredImages, err := r.ListImages(ctx, nil, &ListImagesOptions{Filters: options.Filters}) 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 return nil, rmErrors
} }
for _, img := range filteredImages { for _, img := range filteredImages {
orderedIDs = append(orderedIDs, img.ID()) toDelete = append(toDelete, img.ID())
deleteMap[img.ID()] = &deleteMe{image: img} 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. // Now remove the images in the given order.
rmMap := make(map[string]*RemoveImageReport) rmMap := make(map[string]*RemoveImageReport)
for _, id := range orderedIDs { orderedIDs := []string{}
visitedIDs := make(map[string]bool)
for _, id := range toDelete {
del, exists := deleteMap[id] del, exists := deleteMap[id]
if !exists { if !exists {
appendError(errors.Errorf("internal error: ID %s not in found in image-deletion map", id)) appendError(errors.Errorf("internal error: ID %s not in found in image-deletion map", id))
@ -644,10 +642,18 @@ func (r *Runtime) RemoveImages(ctx context.Context, names []string, options *Rem
del.referencedBy = []string{""} del.referencedBy = []string{""}
} }
for _, ref := range del.referencedBy { 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) appendError(err)
}
// NOTE: make sure to add given ID only once to orderedIDs.
for _, id := range processedIDs {
if visited := visitedIDs[id]; visited {
continue continue
} }
orderedIDs = append(orderedIDs, id)
visitedIDs[id] = true
}
} }
} }

4
vendor/modules.txt vendored
View File

@ -77,7 +77,7 @@ github.com/containernetworking/plugins/pkg/utils/hwaddr
github.com/containernetworking/plugins/pkg/utils/sysctl github.com/containernetworking/plugins/pkg/utils/sysctl
github.com/containernetworking/plugins/plugins/ipam/host-local/backend github.com/containernetworking/plugins/plugins/ipam/host-local/backend
github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator
# github.com/containers/buildah v1.21.1-0.20210707133512-2eb97b499d74 # github.com/containers/buildah v1.21.1-0.20210721171232-54cafea4c933
github.com/containers/buildah github.com/containers/buildah
github.com/containers/buildah/bind github.com/containers/buildah/bind
github.com/containers/buildah/chroot github.com/containers/buildah/chroot
@ -93,7 +93,7 @@ github.com/containers/buildah/pkg/overlay
github.com/containers/buildah/pkg/parse github.com/containers/buildah/pkg/parse
github.com/containers/buildah/pkg/rusage github.com/containers/buildah/pkg/rusage
github.com/containers/buildah/util github.com/containers/buildah/util
# github.com/containers/common v0.41.1-0.20210716140645-ffcfe1ff6e70 # github.com/containers/common v0.41.1-0.20210721172332-291287e9d060
github.com/containers/common/libimage github.com/containers/common/libimage
github.com/containers/common/libimage/manifests github.com/containers/common/libimage/manifests
github.com/containers/common/pkg/apparmor github.com/containers/common/pkg/apparmor