Merge pull request #10983 from vrothberg/fix-dangling

refine dangling checks
This commit is contained in:
OpenShift Merge Robot
2021-07-27 10:57:21 +02:00
committed by GitHub
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).
**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**
Filter by images labels key and/or value.

View File

@ -10,7 +10,7 @@ podman\-rmi - Removes one or more locally stored images
## DESCRIPTION
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

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/containernetworking/cni v0.8.1
github.com/containernetworking/plugins v0.9.1
github.com/containers/buildah v1.21.1-0.20210707133512-2eb97b499d74
github.com/containers/common v0.41.1-0.20210716140645-ffcfe1ff6e70
github.com/containers/buildah v1.21.1-0.20210721171232-54cafea4c933
github.com/containers/common v0.41.1-0.20210721172332-291287e9d060
github.com/containers/conmon v2.0.20+incompatible
github.com/containers/image/v5 v5.13.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.9.1 h1:FD1tADPls2EEi3flPc2OegIY1M9pUa9r2Quag7HMLV8=
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.20210707133512-2eb97b499d74/go.mod h1:P0rxPkI7440rWVMkbPkSHX4j1PxjOcx+cY8QrqYgdLE=
github.com/containers/common v0.40.2-0.20210707094508-0a4a1906d4b2/go.mod h1:thow5Jn7O+rP01njI9COQ16L9g/KQ1LcMcYqP2NhYCU=
github.com/containers/common v0.41.1-0.20210716140645-ffcfe1ff6e70 h1:9kKqg10PfrLQdsN4lw/n8XIuqLk43JwwAcE4klCvOsI=
github.com/containers/common v0.41.1-0.20210716140645-ffcfe1ff6e70/go.mod h1:w0CLPB8nH75msgISbE/z8TMIxK9disFsmvFNd2SDid8=
github.com/containers/buildah v1.21.1-0.20210721171232-54cafea4c933 h1:jqO3hDypBoKM5be+fVcqGHOpX2fOiQy2DFEeb/VKpsk=
github.com/containers/buildah v1.21.1-0.20210721171232-54cafea4c933/go.mod h1:9gspFNeUJxIK72n1IMIKIHmtcePEZQsv0tjo+1LqkCo=
github.com/containers/common v0.41.1-0.20210721112610-c95d2f794edf/go.mod h1:Ba5YVNCnyX6xDtg1JqEHa2EMVMW5UbHmIyEqsEwpeGE=
github.com/containers/common v0.41.1-0.20210721172332-291287e9d060 h1:HgGff2MeEKfYoKp2WQFl9xdsgP7KV8rr/1JZRIuPXmg=
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/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
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/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.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=
@ -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.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
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/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
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.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
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/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
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)
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{
ID: l.ID(),
ParentId: imageData.Parent,
@ -188,7 +193,7 @@ func ImageToImageSummary(l *libimage.Image) (*entities.ImageSummary, error) {
Labels: imageData.Labels,
Containers: containerCount,
ReadOnly: l.IsReadOnly(),
Dangling: l.IsDangling(),
Dangling: isDangling,
Names: l.Names(),
Digest: string(imageData.Digest),
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() {
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{
ID: img.ID(),
// ConfigDigest: string(img.ConfigDigest),
Created: img.Created().Unix(),
Dangling: img.IsDangling(),
Dangling: isDangling,
Digest: string(img.Digest()),
RepoDigests: digests,
History: img.NamesHistory(),

View File

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

View File

@ -30,7 +30,7 @@ env:
UBUNTU_NAME: "ubuntu-2104"
PRIOR_UBUNTU_NAME: "ubuntu-2010"
IMAGE_SUFFIX: "c6534244118822912"
IMAGE_SUFFIX: "c6248193773010944"
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${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
// 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) {
if id, err := strconv.ParseUint(userspec, 10, 32); err == nil {
return uint32(id), uint32(id), nil
var (
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 {
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.Stdin, cmd.Stdout, cmd.Stderr = stdin, stdout, stderr
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)
confwg.Add(1)
@ -207,7 +207,7 @@ func runUsingChrootMain() {
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")
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.Stdin, cmd.Stdout, cmd.Stderr = stdin, stdout, stderr
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
requestedUserNS := false
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
// 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.
if options.Spec == nil {
if options.Spec == nil || options.Spec.Process == nil {
fmt.Fprintf(os.Stderr, "invalid options spec passed in\n")
os.Exit(1)
}
@ -819,7 +819,6 @@ func runUsingChrootExecMain() {
// Output debug messages when that differs from what we're being asked to do.
func logNamespaceDiagnostics(spec *specs.Spec) {
sawMountNS := false
sawUserNS := false
sawUTSNS := false
for _, ns := range spec.Linux.Namespaces {
switch ns.Type {
@ -854,9 +853,8 @@ func logNamespaceDiagnostics(spec *specs.Spec) {
}
case specs.UserNamespace:
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:
if 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 {
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 {
logrus.Debugf("UTS namespace not requested, but creating a new one anyway")
}

View File

@ -4,10 +4,10 @@ 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.5
github.com/containers/storage v1.32.6
github.com/docker/distribution v2.7.1+incompatible
github.com/docker/go-units v0.4.0
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/mattn/go-shellwords v1.0.12
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/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

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.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.17 h1:yFHH5bghP9ij5Y34PPaMOE8g//oXZ0uJQeMENVo2zcI=
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-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY=
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.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=
@ -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/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=
@ -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/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY=
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.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=
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=
@ -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.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=
@ -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.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 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
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=
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=
@ -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-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=

View File

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

View File

@ -165,8 +165,7 @@ func NewExecutor(logger *logrus.Logger, store storage.Store, options define.Buil
if err != nil {
return nil, err
}
transientMounts = append([]Mount{Mount(mount)}, transientMounts...)
transientMounts = append([]Mount{mount}, transientMounts...)
}
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
// filesystem. Individual stages can use them to determine whether or
// 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
for node != nil { // each line
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,
// we might not record the right value here.
b.baseMap[base] = true
logrus.Debugf("base: %q", base)
logrus.Debugf("base for stage %d: %q", stageIndex, base)
}
}
case "ADD", "COPY":
@ -601,7 +600,7 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image
// not record the right value here.
rootfs := strings.TrimPrefix(flag, "--from=")
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,
Args: s.executor.runtimeArgs,
NoPivot: os.Getenv("BUILDAH_NOPIVOT") != "",
Mounts: convertMounts(s.executor.transientMounts),
Mounts: append([]Mount{}, s.executor.transientMounts...),
Env: config.Env,
User: config.User,
WorkingDir: config.WorkingDir,
@ -557,13 +557,6 @@ func (s *StageExecutor) prepare(ctx context.Context, from string, initializeIBCo
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)
if err != nil {
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
// 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,
// 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.
// substitute that image's ID for the base image's name here and force
// the pull policy to "never" to avoid triggering an error when it's
// 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 {
return "", nil, err
}
pullPolicy := s.executor.pullPolicy
s.executor.stagesLock.Lock()
if stageImage, isPreviousStage := s.executor.imageMap[base]; isPreviousStage {
base = stageImage
pullPolicy = define.PullNever
}
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
// the imagebuilder configuration may alter the list of steps we have,
// 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
}
children := stage.Node.Children

View File

@ -2,26 +2,11 @@ package imagebuildah
import (
"github.com/containers/buildah"
"github.com/opencontainers/runtime-spec/specs-go"
)
// InitReexec is a wrapper for buildah.InitReexec(). It should be called at
// the start of main(), and if it returns true, main() should return
// immediately.
// successfully immediately.
func InitReexec() bool {
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
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
higher in order to support the OverlayFS filesystem. The kernel version can be checked
with the 'uname -a' command.
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.
### runc Requirement

View File

@ -15,6 +15,7 @@ import (
"unicode"
"github.com/containers/buildah/define"
"github.com/containers/common/pkg/parse"
"github.com/containers/image/v5/types"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/unshare"
@ -205,13 +206,13 @@ func Volume(volume string) (specs.Mount, error) {
if err := validateVolumeMountHostDir(arr[0]); err != nil {
return mount, err
}
if err := ValidateVolumeCtrDir(arr[1]); err != nil {
if err := parse.ValidateVolumeCtrDir(arr[1]); err != nil {
return mount, err
}
mountOptions := ""
if len(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
}
}
@ -360,7 +361,7 @@ func GetBindMount(args []string) (specs.Mount, error) {
if len(kv) == 1 {
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
}
newMount.Source = kv[1]
@ -369,7 +370,7 @@ func GetBindMount(args []string) (specs.Mount, error) {
if len(kv) == 1 {
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
}
newMount.Destination = kv[1]
@ -391,7 +392,7 @@ func GetBindMount(args []string) (specs.Mount, error) {
newMount.Source = newMount.Destination
}
opts, err := ValidateVolumeOpts(newMount.Options)
opts, err := parse.ValidateVolumeOpts(newMount.Options)
if err != nil {
return newMount, err
}
@ -433,7 +434,7 @@ func GetTmpfsMount(args []string) (specs.Mount, error) {
if len(kv) == 1 {
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
}
newMount.Destination = kv[1]
@ -452,17 +453,7 @@ func GetTmpfsMount(args []string) (specs.Mount, error) {
// ValidateVolumeHostDir validates a volume mount's source directory
func ValidateVolumeHostDir(hostDir string) error {
if len(hostDir) == 0 {
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
return parse.ValidateVolumeHostDir(hostDir)
}
// validates the host path of buildah --volume
@ -478,75 +469,12 @@ func validateVolumeMountHostDir(hostDir string) error {
// ValidateVolumeCtrDir validates a volume mount's destination directory.
func ValidateVolumeCtrDir(ctrDir string) error {
if len(ctrDir) == 0 {
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
return parse.ValidateVolumeCtrDir(ctrDir)
}
// ValidateVolumeOpts validates a volume's options
func ValidateVolumeOpts(options []string) ([]string, error) {
var foundRootPropagation, foundRWRO, foundLabelChange, bindType, foundExec, foundDev, foundSuid, foundChown int
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
return parse.ValidateVolumeOpts(options)
}
// 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")
if err == nil && c.Flag("creds").Changed {
var err error
ctx.DockerAuthConfig, err = getDockerAuth(creds)
ctx.DockerAuthConfig, err = AuthConfig(creds)
if err != nil {
return nil, err
}
@ -734,7 +662,9 @@ func parseCreds(creds string) (string, string) {
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)
if username == "" {
fmt.Print("Username: ")

View File

@ -88,7 +88,7 @@ func (r *Runtime) compileImageFilters(ctx context.Context, filters []string) ([]
if err != nil {
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":
filterFuncs = append(filterFuncs, filterID(value))
@ -201,9 +201,13 @@ func filterContainers(value bool) filterFunc {
}
// 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 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
}
// IsDangling returns true if the image is dangling. An image is considered
// dangling if no names are associated with it in the containers storage.
func (i *Image) IsDangling() bool {
return len(i.Names()) == 0
}
// IsIntermediate returns true if the image is an intermediate image, that is
// a dangling image without children.
func (i *Image) IsIntermediate(ctx context.Context) (bool, error) {
// If the image has tags, it's not an intermediate one.
if !i.IsDangling() {
// IsDangling returns true if the image is dangling, that is an untagged image
// without children.
func (i *Image) IsDangling(ctx context.Context) (bool, error) {
if len(i.Names()) > 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
// an untagged image with children.
func (i *Image) IsIntermediate(ctx context.Context) (bool, error) {
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

View File

@ -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...)

View File

@ -10,6 +10,7 @@ import (
"github.com/containers/common/pkg/config"
registryTransport "github.com/containers/image/v5/docker"
dockerArchiveTransport "github.com/containers/image/v5/docker/archive"
dockerDaemonTransport "github.com/containers/image/v5/docker/daemon"
"github.com/containers/image/v5/docker/reference"
ociArchiveTransport "github.com/containers/image/v5/oci/archive"
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
ref, err := alltransports.ParseImageName(name)
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.
// In fact, we need to since they are not parseable.
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
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():
split := strings.SplitN(ref.StringWithinTransport(), ":", 2)
storageName = toLocalImageName(split[0])

View File

@ -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
}
}
}

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/plugins/ipam/host-local/backend
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/bind
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/rusage
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/manifests
github.com/containers/common/pkg/apparmor