diff options
-rwxr-xr-x | API.md | 10 | ||||
-rw-r--r-- | Makefile | 9 | ||||
-rw-r--r-- | cmd/podman/cliconfig/config.go | 4 | ||||
-rw-r--r-- | cmd/podman/image.go | 1 | ||||
-rw-r--r-- | cmd/podman/main.go | 1 | ||||
-rw-r--r-- | cmd/podman/untag.go | 67 | ||||
-rw-r--r-- | cmd/podman/varlink/io.podman.varlink | 6 | ||||
-rw-r--r-- | commands.md | 2 | ||||
-rwxr-xr-x | contrib/build_rpm.sh | 21 | ||||
-rw-r--r-- | docs/source/markdown/links/podman-image-untag.1 | 1 | ||||
-rw-r--r-- | docs/source/markdown/podman-image.1.md | 1 | ||||
-rw-r--r-- | docs/source/markdown/podman-untag.1.md | 37 | ||||
-rw-r--r-- | docs/source/markdown/podman.1.md | 1 | ||||
-rw-r--r-- | pkg/adapter/runtime_remote.go | 6 | ||||
-rw-r--r-- | pkg/varlinkapi/images.go | 12 | ||||
-rw-r--r-- | test/e2e/untag_test.go | 73 |
16 files changed, 241 insertions, 11 deletions
@@ -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;"> @@ -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)) + }) +}) |