summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xAPI.md10
-rw-r--r--Makefile9
-rw-r--r--cmd/podman/cliconfig/config.go4
-rw-r--r--cmd/podman/image.go1
-rw-r--r--cmd/podman/main.go1
-rw-r--r--cmd/podman/untag.go67
-rw-r--r--cmd/podman/varlink/io.podman.varlink6
-rw-r--r--commands.md2
-rwxr-xr-xcontrib/build_rpm.sh21
-rw-r--r--docs/source/markdown/links/podman-image-untag.11
-rw-r--r--docs/source/markdown/podman-image.1.md1
-rw-r--r--docs/source/markdown/podman-untag.1.md37
-rw-r--r--docs/source/markdown/podman.1.md1
-rw-r--r--pkg/adapter/runtime_remote.go6
-rw-r--r--pkg/varlinkapi/images.go12
-rw-r--r--test/e2e/untag_test.go73
16 files changed, 241 insertions, 11 deletions
diff --git a/API.md b/API.md
index 469dbaa1b..05b08dc6f 100755
--- a/API.md
+++ b/API.md
@@ -185,6 +185,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func UnpausePod(name: string) string](#UnpausePod)
+[func UntagImage(name: string, tag: string) string](#UntagImage)
+
[func VolumeCreate(options: VolumeCreateOpts) string](#VolumeCreate)
[func VolumeRemove(options: VolumeRemoveOpts) []string, map[string]](#VolumeRemove)
@@ -1234,6 +1236,14 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.UnpausePod '{"name": "foo
"pod": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f"
}
~~~
+### <a name="UntagImage"></a>func UntagImage
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method UntagImage(name: [string](https://godoc.org/builtin#string), tag: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div>
+UntagImage takes the name or ID of an image in local storage as well as the
+tag name to be removed. If the image cannot be found, an
+[ImageNotFound](#ImageNotFound) error will be returned; otherwise, the ID of
+the image is returned on success.
### <a name="VolumeCreate"></a>func VolumeCreate
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
diff --git a/Makefile b/Makefile
index 1173f43a0..686a0f957 100644
--- a/Makefile
+++ b/Makefile
@@ -572,10 +572,13 @@ vendor-in-container:
package: ## Build rpm packages
## TODO(ssbarnea): make version number predictable, it should not change
## on each execution, producing duplicates.
- rm -f ~/rpmbuild/RPMS/x86_64/* ~/rpmbuild/RPMS/noarch/*
+ rm -rf build/* *.src.rpm ~/rpmbuild/RPMS/*
./contrib/build_rpm.sh
+# Remember that rpms install exec to /usr/bin/podman while a `make install`
+# installs them to /usr/local/bin/podman which is likely before. Always use
+# a full path to test installed podman or you risk to call another executable.
package-install: package ## Install rpm packages
- sudo ${PKG_MANAGER} -y remove podman podman-remote
- sudo ${PKG_MANAGER} -y clean all
sudo ${PKG_MANAGER} -y install ${HOME}/rpmbuild/RPMS/*/*.rpm
+ /usr/bin/podman version
+ /usr/bin/podman info # will catch a broken conmon
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index 282d90d0b..0e4315411 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -681,3 +681,7 @@ type SystemDfValues struct {
Verbose bool
Format string
}
+
+type UntagValues struct {
+ PodmanCommand
+}
diff --git a/cmd/podman/image.go b/cmd/podman/image.go
index 66c141686..ce576ff4b 100644
--- a/cmd/podman/image.go
+++ b/cmd/podman/image.go
@@ -74,6 +74,7 @@ var imageSubCommands = []*cobra.Command{
_saveCommand,
_tagCommand,
_treeCommand,
+ _untagCommand,
}
func init() {
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index 344170ddd..c727eea85 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -68,6 +68,7 @@ var mainCommands = []*cobra.Command{
imageCommand.Command,
_startCommand,
systemCommand.Command,
+ _untagCommand,
}
var rootCmd = &cobra.Command{
diff --git a/cmd/podman/untag.go b/cmd/podman/untag.go
new file mode 100644
index 000000000..9ff62b808
--- /dev/null
+++ b/cmd/podman/untag.go
@@ -0,0 +1,67 @@
+package main
+
+import (
+ "github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/pkg/adapter"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "github.com/spf13/cobra"
+)
+
+var (
+ untagCommand cliconfig.UntagValues
+
+ _untagCommand = &cobra.Command{
+ Use: "untag [flags] IMAGE [NAME...]",
+ Short: "Remove a name from a local image",
+ Long: "Removes one or more names from a locally-stored image.",
+ RunE: func(cmd *cobra.Command, args []string) error {
+ untagCommand.InputArgs = args
+ untagCommand.GlobalFlags = MainGlobalOpts
+ untagCommand.Remote = remoteclient
+ return untag(&untagCommand)
+ },
+ Example: `podman untag 0e3bbc2
+ podman untag imageID:latest otherImageName:latest
+ podman untag httpd myregistryhost:5000/fedora/httpd:v2`,
+ }
+)
+
+func init() {
+ untagCommand.Command = _untagCommand
+ untagCommand.SetHelpTemplate(HelpTemplate())
+ untagCommand.SetUsageTemplate(UsageTemplate())
+}
+
+func untag(c *cliconfig.UntagValues) error {
+ args := c.InputArgs
+
+ if len(args) == 0 {
+ return errors.Errorf("at least one image name needs to be specified")
+ }
+
+ runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand)
+ if err != nil {
+ return errors.Wrapf(err, "could not create runtime")
+ }
+ defer runtime.DeferredShutdown(false)
+
+ newImage, err := runtime.NewImageFromLocal(args[0])
+ if err != nil {
+ return err
+ }
+
+ tags := args[1:]
+ if len(args) == 1 {
+ // Remove all tags if not explicitly specified
+ tags = newImage.Names()
+ }
+ logrus.Debugf("Tags to be removed: %v", tags)
+
+ for _, tag := range tags {
+ if err := newImage.UntagImage(tag); err != nil {
+ return errors.Wrapf(err, "removing %q from %q", tag, newImage.InputName)
+ }
+ }
+ return nil
+}
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index 1bacd2de6..e1f28d847 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -860,6 +860,12 @@ method PushImage(name: string, tag: string, compress: bool, format: string, remo
# be found, an [ImageNotFound](#ImageNotFound) error will be returned; otherwise, the ID of the image is returned on success.
method TagImage(name: string, tagged: string) -> (image: string)
+# UntagImage takes the name or ID of an image in local storage as well as the
+# tag name to be removed. If the image cannot be found, an
+# [ImageNotFound](#ImageNotFound) error will be returned; otherwise, the ID of
+# the image is returned on success.
+method UntagImage(name: string, tag: string) -> (image: string)
+
# RemoveImage takes the name or ID of an image as well as a boolean that determines if containers using that image
# should be deleted. If the image cannot be found, an [ImageNotFound](#ImageNotFound) error will be returned. The
# ID of the removed image is returned when complete. See also [DeleteUnusedImages](DeleteUnusedImages).
diff --git a/commands.md b/commands.md
index de9169a4b..b744b702e 100644
--- a/commands.md
+++ b/commands.md
@@ -37,7 +37,7 @@
| [podman-import(1)](/docs/source/markdown/podman-import.1.md) | Import a tarball and save it as a filesystem image |
| [podman-info(1)](/docs/source/markdown/podman-info.1.md) | Display system information |
| [podman-init(1)](/docs/source/markdown/podman-init.1.md) | Initialize a container |
-| [podman-inspect(1)](/docs/source/markdown/podman-inspect.1.md) | Display the configuration of a container or image | [![...](/docs/source/markdown/play.png)](https://asciinema.org/a/133418) |
+| [podman-inspect(1)](/docs/source/markdown/podman-inspect.1.md) | Display the configuration of a container or image | [![...](/docs/source/markdown/play.png)](https://podman.io/asciinema/podman/inspect/) | [Here](https://github.com/containers/Demos/blob/master/podman_cli/podman_inspect.sh) |
| [podman-kill(1)](/docs/source/markdown/podman-kill.1.md) | Kill the main process in one or more running containers |
| [podman-load(1)](/docs/source/markdown/podman-load.1.md) | Load an image from a container image archive |
| [podman-login(1)](/docs/source/markdown/podman-login.1.md) | Login to a container registry |
diff --git a/contrib/build_rpm.sh b/contrib/build_rpm.sh
index b64973f0d..b162a9c88 100755
--- a/contrib/build_rpm.sh
+++ b/contrib/build_rpm.sh
@@ -2,7 +2,7 @@
set -euxo pipefail
# returned path can vary: /usr/bin/dnf /bin/dnf ...
-pkg_manager=`command -v dnf yum | head -n1`
+pkg_manager=$(command -v dnf yum | head -n1)
echo "Package manager binary: $pkg_manager"
@@ -14,25 +14,32 @@ enabled=1
gpgcheck=0" > /etc/yum.repos.d/container_virt.repo
fi
-declare -a PKGS=(device-mapper-devel \
+declare -a PKGS=(\
+ createrepo \
+ device-mapper-devel \
git \
glib2-devel \
glibc-static \
+ go-compilers-golang-compiler \
golang \
gpgme-devel \
libassuan-devel \
libseccomp-devel \
libselinux-devel \
make \
+ redhat-rpm-config \
rpm-build \
- go-compilers-golang-compiler \
+ rpmdevtools \
systemd-devel \
)
if [[ $pkg_manager == *dnf ]]; then
# We need to enable PowerTools if we want to get
# install all the pkgs we define in PKGS
- sudo dnf config-manager --set-enabled PowerTools
+ # PowerTools exists on centos-8 but not on fedora-30 and rhel-8
+ if (dnf -v -C repolist all|grep "Repo-id : PowerTools" >/dev/null); then
+ sudo dnf config-manager --set-enabled PowerTools
+ fi
PKGS+=(python3-devel \
python3-varlink \
@@ -53,6 +60,9 @@ export extra_arg="--without doc --without debug"
echo ${PKGS[*]}
sudo $pkg_manager install -y ${PKGS[*]}
+# clean up src.rpm as it's been built
+sudo rm -f podman-*.src.rpm
+
make -f .copr/Makefile
# workaround for https://github.com/containers/libpod/issues/4627
if [ -d ~/rpmbuild/BUILD ]; then
@@ -60,6 +70,3 @@ if [ -d ~/rpmbuild/BUILD ]; then
fi
rpmbuild --rebuild ${extra_arg:-} podman-*.src.rpm
-
-# clean up src.rpm as it's been built
-sudo rm -f podman-*.src.rpm
diff --git a/docs/source/markdown/links/podman-image-untag.1 b/docs/source/markdown/links/podman-image-untag.1
new file mode 100644
index 000000000..3ec6a8a68
--- /dev/null
+++ b/docs/source/markdown/links/podman-image-untag.1
@@ -0,0 +1 @@
+.so man1/podman-untag.1
diff --git a/docs/source/markdown/podman-image.1.md b/docs/source/markdown/podman-image.1.md
index 339a531dd..1552098ac 100644
--- a/docs/source/markdown/podman-image.1.md
+++ b/docs/source/markdown/podman-image.1.md
@@ -27,6 +27,7 @@ The image command allows you to manage images
| save | [podman-save(1)](podman-save.1.md) | Save an image to docker-archive or oci. |
| sign | [podman-image-sign(1)](podman-image-sign.1.md) | Create a signature for an image. |
| tag | [podman-tag(1)](podman-tag.1.md) | Add an additional name to a local image. |
+| untag | [podman-untag(1)](podman-untag.1.md) | Removes one or more names from a locally-stored image. |
| tree | [podman-image-tree(1)](podman-image-tree.1.md) | Prints layer hierarchy of an image in a tree format. |
| trust | [podman-image-trust(1)](podman-image-trust.1.md)| Manage container registry image trust policy. |
diff --git a/docs/source/markdown/podman-untag.1.md b/docs/source/markdown/podman-untag.1.md
new file mode 100644
index 000000000..3a54283d6
--- /dev/null
+++ b/docs/source/markdown/podman-untag.1.md
@@ -0,0 +1,37 @@
+% podman-untag(1)
+
+## NAME
+podman\-untag - Removes one or more names from a locally-stored image
+
+## SYNOPSIS
+**podman untag** *image*[:*tag*] [*target-names*[:*tag*]] [*options*]
+
+**podman image untag** *image*[:*tag*] [target-names[:*tag*]] [*options*]
+
+## DESCRIPTION
+Removes one or all names of an image. A name refers to the entire image name,
+including the optional *tag* after the `:`. If no target image names are
+specified, `untag` will remove all tags for the image at once.
+
+## OPTIONS
+
+**--help**, **-h**
+
+Print usage statement
+
+## EXAMPLES
+
+```
+$ podman untag 0e3bbc2
+
+$ podman untag imageName:latest otherImageName:latest
+
+$ podman untag httpd myregistryhost:5000/fedora/httpd:v2
+```
+
+
+## SEE ALSO
+podman(1)
+
+## HISTORY
+December 2019, Originally compiled by Sascha Grunert <sgrunert@suse.com>
diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md
index 01c750144..0c9ec3d1c 100644
--- a/docs/source/markdown/podman.1.md
+++ b/docs/source/markdown/podman.1.md
@@ -201,6 +201,7 @@ the exit codes follow the `chroot` standard, see below:
| [podman-umount(1)](podman-umount.1.md) | Unmount a working container's root filesystem. |
| [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. |
| [podman-unshare(1)](podman-unshare.1.md) | Run a command inside of a modified user namespace. |
+| [podman-untag(1)](podman-untag.1.md) | Removes one or more names from a locally-stored image. |
| [podman-varlink(1)](podman-varlink.1.md) | Runs the varlink backend interface. |
| [podman-version(1)](podman-version.1.md) | Display the Podman version information. |
| [podman-volume(1)](podman-volume.1.md) | Simple management tool for volumes. |
diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go
index fe5cc4fef..9c10b31c0 100644
--- a/pkg/adapter/runtime_remote.go
+++ b/pkg/adapter/runtime_remote.go
@@ -413,6 +413,12 @@ func (ci *ContainerImage) TagImage(tag string) error {
return err
}
+// UntagImage removes a single tag from an image
+func (ci *ContainerImage) UntagImage(tag string) error {
+ _, err := iopodman.UntagImage().Call(ci.Runtime.Conn, ci.ID(), tag)
+ return err
+}
+
// RemoveImage calls varlink to remove an image
func (r *LocalRuntime) RemoveImage(ctx context.Context, img *ContainerImage, force bool) (*image.ImageDeleteResponse, error) {
ir := image.ImageDeleteResponse{}
diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go
index ac92343d0..bc644f87c 100644
--- a/pkg/varlinkapi/images.go
+++ b/pkg/varlinkapi/images.go
@@ -450,6 +450,18 @@ func (i *LibpodAPI) TagImage(call iopodman.VarlinkCall, name, tag string) error
return call.ReplyTagImage(newImage.ID())
}
+// UntagImage accepts an image name and tag as strings and removes the tag from the local store.
+func (i *LibpodAPI) UntagImage(call iopodman.VarlinkCall, name, tag string) error {
+ newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name)
+ if err != nil {
+ return call.ReplyImageNotFound(name, err.Error())
+ }
+ if err := newImage.UntagImage(tag); err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ return call.ReplyUntagImage(newImage.ID())
+}
+
// RemoveImage accepts a image name or ID as a string and force bool to determine if it should
// remove the image even if being used by stopped containers
func (i *LibpodAPI) RemoveImage(call iopodman.VarlinkCall, name string, force bool) error {
diff --git a/test/e2e/untag_test.go b/test/e2e/untag_test.go
new file mode 100644
index 000000000..17171cd41
--- /dev/null
+++ b/test/e2e/untag_test.go
@@ -0,0 +1,73 @@
+package integration
+
+import (
+ "os"
+
+ . "github.com/containers/libpod/test/utils"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Podman untag", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest *PodmanTestIntegration
+ )
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanTestCreate(tempdir)
+ podmanTest.Setup()
+ 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() {
+ podmanTest.Cleanup()
+ f := CurrentGinkgoTestDescription()
+ processTestResult(f)
+
+ })
+
+ It("podman untag all", func() {
+ session := podmanTest.PodmanNoCache([]string{"untag", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ results := podmanTest.PodmanNoCache([]string{"images", ALPINE})
+ results.WaitWithDefaultTimeout()
+ Expect(results.ExitCode()).To(Equal(0))
+ Expect(results.OutputToStringArray()).To(HaveLen(1))
+ })
+
+ It("podman untag single", func() {
+ session := podmanTest.PodmanNoCache([]string{"untag", ALPINE, "localhost/test:latest"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ results := podmanTest.PodmanNoCache([]string{"images"})
+ results.WaitWithDefaultTimeout()
+ Expect(results.ExitCode()).To(Equal(0))
+ Expect(results.OutputToStringArray()).To(HaveLen(5))
+ Expect(results.LineInOuputStartsWith("docker.io/library/alpine")).To(BeTrue())
+ 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() {
+ session := podmanTest.PodmanNoCache([]string{"untag"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).NotTo(Equal(0))
+ })
+})