podman untag: error if tag doesn't exist
Throw an error if a specified tag does not exist. Also make sure that the user input is normalized as we already do for `podman tag`. To prevent regressions, add a set of end-to-end and systemd tests. Last but not least, update the docs and add bash completions. Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
parent
0d26b8f24b
commit
1c6c12581c
|
|
@ -1572,6 +1572,11 @@ _podman_image_tag() {
|
||||||
_podman_tag
|
_podman_tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_podman_image_untag() {
|
||||||
|
_podman_untag
|
||||||
|
}
|
||||||
|
|
||||||
_podman_image() {
|
_podman_image() {
|
||||||
local boolean_options="
|
local boolean_options="
|
||||||
--help
|
--help
|
||||||
|
|
@ -1593,6 +1598,7 @@ _podman_image() {
|
||||||
sign
|
sign
|
||||||
tag
|
tag
|
||||||
trust
|
trust
|
||||||
|
untag
|
||||||
"
|
"
|
||||||
local aliases="
|
local aliases="
|
||||||
list
|
list
|
||||||
|
|
@ -2460,6 +2466,23 @@ _podman_tag() {
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_podman_untag() {
|
||||||
|
local options_with_args="
|
||||||
|
"
|
||||||
|
local boolean_options="
|
||||||
|
--help
|
||||||
|
-h
|
||||||
|
"
|
||||||
|
case "$cur" in
|
||||||
|
-*)
|
||||||
|
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
__podman_complete_images
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
__podman_top_descriptors() {
|
__podman_top_descriptors() {
|
||||||
podman top --list-descriptors
|
podman top --list-descriptors
|
||||||
}
|
}
|
||||||
|
|
@ -3588,6 +3611,7 @@ _podman_podman() {
|
||||||
umount
|
umount
|
||||||
unmount
|
unmount
|
||||||
unpause
|
unpause
|
||||||
|
untag
|
||||||
varlink
|
varlink
|
||||||
version
|
version
|
||||||
volume
|
volume
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,12 @@
|
||||||
podman\-untag - Removes one or more names from a locally-stored image
|
podman\-untag - Removes one or more names from a locally-stored image
|
||||||
|
|
||||||
## SYNOPSIS
|
## SYNOPSIS
|
||||||
**podman untag** *image*[:*tag*] [*target-names*[:*tag*]] [*options*]
|
**podman untag** [*options*] *image* [*name*[:*tag*]...]
|
||||||
|
|
||||||
**podman image untag** *image*[:*tag*] [target-names[:*tag*]] [*options*]
|
**podman image untag** [*options*] *image* [*name*[:*tag*]...]
|
||||||
|
|
||||||
## DESCRIPTION
|
## DESCRIPTION
|
||||||
Removes one or all names of an image. A name refers to the entire image name,
|
Remove one or more names from an image in the local storage. The image can be referred to by ID or reference. If a no name is specified, all names are removed the image. If a specified name is a short name and does not include a registry `localhost/` will be prefixed (e.g., `fedora` -> `localhost/fedora`). If a specified name does not include a tag `:latest` will be appended (e.g., `localhost/fedora` -> `localhost/fedora:latest`).
|
||||||
including the optional *tag* after the `:`. If no target image names are
|
|
||||||
specified, `untag` will remove all tags for the image at once.
|
|
||||||
|
|
||||||
## OPTIONS
|
## OPTIONS
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,9 @@ var (
|
||||||
// ErrNoSuchImage indicates the requested image does not exist
|
// ErrNoSuchImage indicates the requested image does not exist
|
||||||
ErrNoSuchImage = image.ErrNoSuchImage
|
ErrNoSuchImage = image.ErrNoSuchImage
|
||||||
|
|
||||||
|
// ErrNoSuchTag indicates the requested image tag does not exist
|
||||||
|
ErrNoSuchTag = image.ErrNoSuchTag
|
||||||
|
|
||||||
// ErrNoSuchVolume indicates the requested volume does not exist
|
// ErrNoSuchVolume indicates the requested volume does not exist
|
||||||
ErrNoSuchVolume = errors.New("no such volume")
|
ErrNoSuchVolume = errors.New("no such volume")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,4 +12,6 @@ var (
|
||||||
ErrNoSuchPod = errors.New("no such pod")
|
ErrNoSuchPod = errors.New("no such pod")
|
||||||
// ErrNoSuchImage indicates the requested image does not exist
|
// ErrNoSuchImage indicates the requested image does not exist
|
||||||
ErrNoSuchImage = errors.New("no such image")
|
ErrNoSuchImage = errors.New("no such image")
|
||||||
|
// ErrNoSuchTag indicates the requested image tag does not exist
|
||||||
|
ErrNoSuchTag = errors.New("no such tag")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -559,15 +559,24 @@ func (i *Image) TagImage(tag string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UntagImage removes a tag from the given image
|
// UntagImage removes the specified tag from the image.
|
||||||
|
// If the tag does not exist, ErrNoSuchTag is returned.
|
||||||
func (i *Image) UntagImage(tag string) error {
|
func (i *Image) UntagImage(tag string) error {
|
||||||
if err := i.reloadImage(); err != nil {
|
if err := i.reloadImage(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normalize the tag as we do with TagImage.
|
||||||
|
ref, err := NormalizedTag(tag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tag = ref.String()
|
||||||
|
|
||||||
var newTags []string
|
var newTags []string
|
||||||
tags := i.Names()
|
tags := i.Names()
|
||||||
if !util.StringInSlice(tag, tags) {
|
if !util.StringInSlice(tag, tags) {
|
||||||
return nil
|
return errors.Wrapf(ErrNoSuchTag, "%q", tag)
|
||||||
}
|
}
|
||||||
for _, t := range tags {
|
for _, t := range tags {
|
||||||
if tag != t {
|
if tag != t {
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,6 @@ var _ = Describe("Podman untag", func() {
|
||||||
podmanTest = PodmanTestCreate(tempdir)
|
podmanTest = PodmanTestCreate(tempdir)
|
||||||
podmanTest.Setup()
|
podmanTest.Setup()
|
||||||
podmanTest.RestoreAllArtifacts()
|
podmanTest.RestoreAllArtifacts()
|
||||||
|
|
||||||
for _, tag := range []string{"test", "foo", "bar"} {
|
|
||||||
session := podmanTest.PodmanNoCache([]string{"tag", ALPINE, tag})
|
|
||||||
session.WaitWithDefaultTimeout()
|
|
||||||
Expect(session.ExitCode()).To(Equal(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
AfterEach(func() {
|
AfterEach(func() {
|
||||||
|
|
@ -40,34 +33,63 @@ var _ = Describe("Podman untag", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman untag all", func() {
|
It("podman untag all", func() {
|
||||||
session := podmanTest.PodmanNoCache([]string{"untag", ALPINE})
|
tags := []string{ALPINE, "registry.com/foo:bar", "localhost/foo:bar"}
|
||||||
|
|
||||||
|
cmd := []string{"tag"}
|
||||||
|
cmd = append(cmd, tags...)
|
||||||
|
session := podmanTest.PodmanNoCache(cmd)
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
Expect(session.ExitCode()).To(Equal(0))
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
results := podmanTest.PodmanNoCache([]string{"images", ALPINE})
|
// Make sure that all tags exists.
|
||||||
results.WaitWithDefaultTimeout()
|
for _, t := range tags {
|
||||||
Expect(results.ExitCode()).To(Equal(0))
|
session = podmanTest.PodmanNoCache([]string{"image", "exists", t})
|
||||||
Expect(results.OutputToStringArray()).To(HaveLen(1))
|
session.WaitWithDefaultTimeout()
|
||||||
})
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
}
|
||||||
|
|
||||||
It("podman untag single", func() {
|
// No arguments -> remove all tags.
|
||||||
session := podmanTest.PodmanNoCache([]string{"untag", ALPINE, "localhost/test:latest"})
|
session = podmanTest.PodmanNoCache([]string{"untag", ALPINE})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
Expect(session.ExitCode()).To(Equal(0))
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
results := podmanTest.PodmanNoCache([]string{"images"})
|
// Make sure that none of tags exists anymore.
|
||||||
results.WaitWithDefaultTimeout()
|
for _, t := range tags {
|
||||||
Expect(results.ExitCode()).To(Equal(0))
|
session = podmanTest.PodmanNoCache([]string{"image", "exists", t})
|
||||||
Expect(results.OutputToStringArray()).To(HaveLen(6))
|
session.WaitWithDefaultTimeout()
|
||||||
Expect(results.LineInOuputStartsWith("docker.io/library/alpine")).To(BeTrue())
|
Expect(session.ExitCode()).To(Equal(1))
|
||||||
Expect(results.LineInOuputStartsWith("localhost/foo")).To(BeTrue())
|
}
|
||||||
Expect(results.LineInOuputStartsWith("localhost/bar")).To(BeTrue())
|
|
||||||
Expect(results.LineInOuputStartsWith("localhost/test")).To(BeFalse())
|
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman untag not enough arguments", func() {
|
It("podman tag/untag - tag normalization", func() {
|
||||||
session := podmanTest.PodmanNoCache([]string{"untag"})
|
tests := []struct {
|
||||||
session.WaitWithDefaultTimeout()
|
tag, normalized string
|
||||||
Expect(session.ExitCode()).NotTo(Equal(0))
|
}{
|
||||||
|
{"registry.com/image:latest", "registry.com/image:latest"},
|
||||||
|
{"registry.com/image", "registry.com/image:latest"},
|
||||||
|
{"image:latest", "localhost/image:latest"},
|
||||||
|
{"image", "localhost/image:latest"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that the user input is normalized correctly for
|
||||||
|
// `podman tag` and `podman untag`.
|
||||||
|
for _, tt := range tests {
|
||||||
|
session := podmanTest.PodmanNoCache([]string{"tag", ALPINE, tt.tag})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
session = podmanTest.PodmanNoCache([]string{"image", "exists", tt.normalized})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
session = podmanTest.PodmanNoCache([]string{"untag", ALPINE, tt.tag})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
session = podmanTest.PodmanNoCache([]string{"image", "exists", tt.normalized})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(1))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
# helper function for "podman tag/untag" test
|
||||||
|
function _tag_and_check() {
|
||||||
|
local tag_as="$1"
|
||||||
|
local check_as="$2"
|
||||||
|
|
||||||
|
run_podman tag $IMAGE $tag_as
|
||||||
|
run_podman image exists $check_as
|
||||||
|
run_podman untag $IMAGE $check_as
|
||||||
|
run_podman 1 image exists $check_as
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "podman tag/untag" {
|
||||||
|
# Test a fully-qualified image reference.
|
||||||
|
_tag_and_check registry.com/image:latest registry.com/image:latest
|
||||||
|
|
||||||
|
# Test a reference without tag and make sure ":latest" is appended.
|
||||||
|
_tag_and_check registry.com/image registry.com/image:latest
|
||||||
|
|
||||||
|
# Test a tagged short image and make sure "localhost/" is prepended.
|
||||||
|
_tag_and_check image:latest localhost/image:latest
|
||||||
|
|
||||||
|
# Test a short image without tag and make sure "localhost/" is
|
||||||
|
# prepended and ":latest" is appended.
|
||||||
|
_tag_and_check image localhost/image:latest
|
||||||
|
|
||||||
|
# Test error case.
|
||||||
|
run_podman 125 untag $IMAGE registry.com/foo:bar
|
||||||
|
is "$output" "Error: \"registry.com/foo:bar\": no such tag"
|
||||||
|
}
|
||||||
|
|
||||||
|
# vim: filetype=sh
|
||||||
Loading…
Reference in New Issue