diff options
-rw-r--r-- | RELEASE_NOTES.md | 6 | ||||
-rw-r--r-- | changelog.txt | 92 | ||||
-rw-r--r-- | cmd/podman/common/completion.go | 6 | ||||
-rw-r--r-- | contrib/spec/podman.spec.in | 2 | ||||
-rw-r--r-- | libpod/container_inspect.go | 4 | ||||
-rw-r--r-- | libpod/container_internal.go | 75 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 17 | ||||
-rw-r--r-- | libpod/define/container_inspect.go | 4 | ||||
-rw-r--r-- | libpod/volume.go | 11 | ||||
-rw-r--r-- | pkg/domain/infra/abi/images.go | 17 | ||||
-rw-r--r-- | test/e2e/inspect_test.go | 24 | ||||
-rw-r--r-- | test/e2e/rmi_test.go | 30 | ||||
-rw-r--r-- | test/e2e/run_volume_test.go | 18 | ||||
-rw-r--r-- | version/version.go | 2 |
14 files changed, 255 insertions, 53 deletions
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b4823e1d2..17d90aeca 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -112,6 +112,10 @@ - Fixed a bug where failures during the resizing of a container's TTY would print the wrong error. - Fixed a bug where the `podman network disconnect` command could cause the `podman inspect` command to fail for a container until it was restarted ([#9234](https://github.com/containers/podman/issues/9234)). - Fixed a bug where containers created from a read-only rootfs (using the `--rootfs` option to `podman create` and `podman run`) would fail ([#9230](https://github.com/containers/podman/issues/9230)). +- Fixed a bug where specifying Go templates to the `--format` option to multiple Podman commands did not support the `join` function ([#8773](https://github.com/containers/podman/issues/8773)). +- Fixed a bug where the `podman rmi` command could, when run in parallel on multiple images, return `layer not known` errors ([#6510](https://github.com/containers/podman/issues/6510)). +- Fixed a bug where the `podman inspect` command on containers displayed unlimited ulimits incorrectly ([#9303](https://github.com/containers/podman/issues/9303)). +- Fixed a bug where Podman would fail to start when a volume was mounted over a directory in a container that contained symlinks that terminated outside the directory and its subdirectories ([#6003](https://github.com/containers/podman/issues/6003)). ### API - Libpod API version has been bumped to v3.0.0. @@ -134,10 +138,12 @@ - Fixed a bug where the Compat Load API for Images did not properly clean up temporary files. - Fixed a bug where the Compat Create API for Networks could panic when an empty IPAM configuration was specified. - Fixed a bug where the Compat Inspect and List APIs for Networks did not include Scope. +- Fixed a bug where the Compat Wait endpoint for Containers did not support the same wait conditions that Docker did. ### Misc - Updated Buildah to v1.19.2 - Updated the containers/storage library to v1.24.5 +- Updated the containers/image library to v5.10.2 - Updated the containers/common library to v0.33.4 ## v2.2.1 diff --git a/changelog.txt b/changelog.txt index 71af154a6..7b5fb1848 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,95 @@ +- Changelog for v3.0.0 (2021-02-11): + * Update release notes for v3.0 final + * Rewrite copy-up to use buildah Copier + * Display correct value for unlimited ulimit + * make `podman rmi` more robust + * Allow path completion for podman create/run --rootfs + * vendor github.com/containers/image v5.10.2 + * Add default template functions + * Fix per review request + * Increase timeouts in some tests + * Add test for Docker APIv2 wait + * Implement Docker wait conditions + * Improve ContainerEngine.ContainerWait() + * Improve container libpod.Wait*() functions + * [NO TESTS NEEDED] style: indendation + * [NO TESTS NEEDED] fixup: remove debug code + * [NO TESTS NEEDED] Generated files + * Cleanup bindings for image pull + * [NO TESTS NEEDED] Improve generator + * Fix invalid wait condition on kill + * Switch podman image push handlers to use abi + * podman-remote ps --external --pod --sort do not work. + * Fix --arch and --os flags to work correctly + * Switch podman stop/kill/wait handlers to use abi + * Fix handling of container remove + * Bump to v3.0.0-dev + * Bump to v3.0.0-RC3 + * Further release notes for v3.0 + * Bump remote API version to 3.0.0 + * fix logic when not creating a workdir + * play kube selinux test case + * play kube selinux test case + * play kube selinux label test case + * play kube selinux label issue + * Fix podman network disconnect wrong NetworkStatus number + * Bump to v0.33.4 + * Bump github.com/containernetworking/cni from 0.8.0 to 0.8.1 + * Update release notes for v3.0.0-RC3 + * generate kube: handle entrypoint + * Allow pods to use --net=none + * Report StatusConflict on Pod opt partial failures + * bindings: attach: warn correct error + * Make slirp MTU configurable (network_cmd_options) + * Fix podman generate systemd --new special char handling + * Fix --network parsing for podman pod create + * Endpoint that lists containers does not return correct Status value + * Docker ignores mount flags that begin with constency + * podman generate kube ignores --network=host + * Honor custom DNS in play|generate kube + * Don't fail if one of the cgroups is not setup + * Fix #9100 Change console mode message to debug + * Update release notes from master + * libpod: move slirp magic IPs to consts + * rootlessport: set source IP to slirp4netns device + * Bump rootless-containers/rootlesskit to v0.12.0 + * Gating tests: diff test: workaround for RHEL8 failure + * Bump to containers/common v0.33.3 + * add macvlan as a supported network driver + * podman build --pull: refine help message and docs + * Cirrus: Build static podman-remote + * Fix static build cache by using cachix + * Cirrus: Fix running Validate task on branches + * Bump to v3.0.0-dev + * Bump to v3.0.0-RC2 + * Cirrus: add bindings checks + * make bindings generation explicit + * make bindings generation more robuts + * simplify bindings generation + * make: generate bindings: use vendor + * Make generate systemd --new robust against double curly braces + * Ensure shutdown handler access is syncronized + * workdir presence checks + * libpod: add (*Container).ResolvePath() + * Add default net info in container inspect + * Fix podman history --no-trunc for the CREATED BY field + * Ensure the Volumes field in Compat Create is honored + * remote exec: write conmon error on hijacked connection + * Add support for rootless network-aliases + * Allow static ip and mac with rootless cni network + * [CI:DOCS]Correct static API html path + * disable dnsname when --internal + * Use random network names in the e2e tests + * [v3.0] fix RHEL gating test: the /sys thing + * Bump golang.org/x/crypto v3.0 + * Set log driver for compatability containers + * [CI:DOCS] Cirrus: Skip smoke task on branch-push + * [CI:DOCS]Do not run compose tests with CI:DOCS + * Add binding options for container|pod exists + * runner.sh : deal with bash 'set -e' + * [CI:DOCS]Add readthedoc link for 3.0 docs + * [CI:DOCS]Add static HTML for api docs for v3.0 + - Changelog for v3.0.0-rc1 (2021-01-17): * Create release notes for V3.0.0 * Rename AutocompletePortCommand func diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index 09dd74e20..92163efde 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -426,6 +426,12 @@ func AutocompleteCreateRun(cmd *cobra.Command, args []string, toComplete string) return nil, cobra.ShellCompDirectiveNoFileComp } if len(args) < 1 { + // check if the rootfs flag is set + // if it is set to true provide directory completion + rootfs, err := cmd.Flags().GetBool("rootfs") + if err == nil && rootfs { + return nil, cobra.ShellCompDirectiveFilterDirs + } return getImages(cmd, toComplete) } // TODO: add path completion for files in the image diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in index db79ebede..fce2afbeb 100644 --- a/contrib/spec/podman.spec.in +++ b/contrib/spec/podman.spec.in @@ -42,7 +42,7 @@ Epoch: 99 %else Epoch: 0 %endif -Version: 3.0.0 +Version: 3.0.1 Release: #COMMITDATE#.git%{shortcommit0}%{?dist} Summary: Manage Pods, Containers and Container Images License: ASL 2.0 diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index ac7eae56b..cc8b75472 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -864,8 +864,8 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named for _, limit := range ctrSpec.Process.Rlimits { newLimit := define.InspectUlimit{} newLimit.Name = limit.Type - newLimit.Soft = limit.Soft - newLimit.Hard = limit.Hard + newLimit.Soft = int64(limit.Soft) + newLimit.Hard = int64(limit.Hard) hostConfig.Ulimits = append(hostConfig.Ulimits, newLimit) } } diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 5a61f7fe6..15958471f 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -13,6 +13,7 @@ import ( "strings" "time" + "github.com/containers/buildah/copier" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/events" "github.com/containers/podman/v2/pkg/cgroups" @@ -1580,18 +1581,8 @@ func (c *Container) mountNamedVolume(v *ContainerNamedVolume, mountpoint string) return nil, err } - // HACK HACK HACK - copy up into a volume driver is 100% broken - // right now. - if vol.UsesVolumeDriver() { - logrus.Infof("Not copying up into volume %s as it uses a volume driver", vol.Name()) - return vol, nil - } - // If the volume is not empty, we should not copy up. - volMount, err := vol.MountPoint() - if err != nil { - return nil, err - } + volMount := vol.mountPoint() contents, err := ioutil.ReadDir(volMount) if err != nil { return nil, errors.Wrapf(err, "error listing contents of volume %s mountpoint when copying up from container %s", vol.Name(), c.ID()) @@ -1607,8 +1598,55 @@ func (c *Container) mountNamedVolume(v *ContainerNamedVolume, mountpoint string) if err != nil { return nil, errors.Wrapf(err, "error calculating destination path to copy up container %s volume %s", c.ID(), vol.Name()) } - if err := c.copyWithTarFromImage(srcDir, volMount); err != nil && !os.IsNotExist(err) { - return nil, errors.Wrapf(err, "error copying content from container %s into volume %s", c.ID(), vol.Name()) + // Do a manual stat on the source directory to verify existence. + // Skip the rest if it exists. + // TODO: Should this be stat or lstat? I'm using lstat because I + // think copy-up doesn't happen when the source is a link. + srcStat, err := os.Lstat(srcDir) + if err != nil { + if os.IsNotExist(err) { + // Source does not exist, don't bother copying + // up. + return vol, nil + } + return nil, errors.Wrapf(err, "error identifying source directory for copy up into volume %s", vol.Name()) + } + // If it's not a directory we're mounting over it. + if !srcStat.IsDir() { + return vol, nil + } + + // Buildah Copier accepts a reader, so we'll need a pipe. + reader, writer := io.Pipe() + defer reader.Close() + + errChan := make(chan error, 1) + + logrus.Infof("About to copy up into volume %s", vol.Name()) + + // Copy, container side: get a tar archive of what needs to be + // streamed into the volume. + go func() { + defer writer.Close() + getOptions := copier.GetOptions{ + KeepDirectoryNames: false, + } + errChan <- copier.Get(mountpoint, "", getOptions, []string{v.Dest + "/."}, writer) + }() + + // Copy, volume side: stream what we've written to the pipe, into + // the volume. + copyOpts := copier.PutOptions{} + if err := copier.Put(volMount, "", copyOpts, reader); err != nil { + err2 := <-errChan + if err2 != nil { + logrus.Errorf("Error streaming contents of container %s directory for volume copy-up: %v", c.ID(), err2) + } + return nil, errors.Wrapf(err, "error copying up to volume %s", vol.Name()) + } + + if err := <-errChan; err != nil { + return nil, errors.Wrapf(err, "error streaming container content for copy up into volume %s", vol.Name()) } } return vol, nil @@ -2058,17 +2096,6 @@ func (c *Container) unmount(force bool) error { return nil } -// this should be from chrootarchive. -// Container MUST be mounted before calling. -func (c *Container) copyWithTarFromImage(source, dest string) error { - mappings := idtools.NewIDMappingsFromMaps(c.config.IDMappings.UIDMap, c.config.IDMappings.GIDMap) - a := archive.NewArchiver(mappings) - if err := c.copyOwnerAndPerms(source, dest); err != nil { - return err - } - return a.CopyWithTar(source, dest) -} - // checkReadyForRemoval checks whether the given container is ready to be // removed. // These checks are only used if force-remove is not specified. diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index ba85a1f47..952cc42d1 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -2261,23 +2261,6 @@ func (c *Container) generatePasswdAndGroup() (string, string, error) { return passwdPath, groupPath, nil } -func (c *Container) copyOwnerAndPerms(source, dest string) error { - info, err := os.Stat(source) - if err != nil { - if os.IsNotExist(err) { - return nil - } - return err - } - if err := os.Chmod(dest, info.Mode()); err != nil { - return err - } - if err := os.Chown(dest, int(info.Sys().(*syscall.Stat_t).Uid), int(info.Sys().(*syscall.Stat_t).Gid)); err != nil { - return err - } - return nil -} - // Get cgroup path in a format suitable for the OCI spec func (c *Container) getOCICgroupPath() (string, error) { unified, err := cgroups.IsCgroup2UnifiedMode() diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go index 9a93e2ffd..7d4b8a46f 100644 --- a/libpod/define/container_inspect.go +++ b/libpod/define/container_inspect.go @@ -120,9 +120,9 @@ type InspectUlimit struct { // Name is the name (type) of the ulimit. Name string `json:"Name"` // Soft is the soft limit that will be applied. - Soft uint64 `json:"Soft"` + Soft int64 `json:"Soft"` // Hard is the hard limit that will be applied. - Hard uint64 `json:"Hard"` + Hard int64 `json:"Hard"` } // InspectDevice is a single device that will be mounted into the container. diff --git a/libpod/volume.go b/libpod/volume.go index 4c137cb8e..5cc5e7e40 100644 --- a/libpod/volume.go +++ b/libpod/volume.go @@ -130,11 +130,18 @@ func (v *Volume) MountPoint() (string, error) { if err := v.update(); err != nil { return "", err } + } + + return v.mountPoint(), nil +} - return v.state.MountPoint, nil +// Internal-only helper for volume mountpoint +func (v *Volume) mountPoint() string { + if v.UsesVolumeDriver() { + return v.state.MountPoint } - return v.config.MountPoint, nil + return v.config.MountPoint } // Options return the volume's options diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 8ca93e770..f2d0f2c39 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -580,12 +580,21 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie // without having to pass all local data around. deleteImage := func(img *image.Image) error { results, err := ir.Libpod.RemoveImage(ctx, img, opts.Force) - if err != nil { + switch errors.Cause(err) { + case nil: + // Removal worked, so let's report it. + report.Deleted = append(report.Deleted, results.Deleted) + report.Untagged = append(report.Untagged, results.Untagged...) + return nil + case storage.ErrImageUnknown: + // The image must have been removed already (see #6510). + report.Deleted = append(report.Deleted, img.ID()) + report.Untagged = append(report.Untagged, img.ID()) + return nil + default: + // Fatal error. return err } - report.Deleted = append(report.Deleted, results.Deleted) - report.Untagged = append(report.Untagged, results.Untagged...) - return nil } // Delete all images from the local storage. diff --git a/test/e2e/inspect_test.go b/test/e2e/inspect_test.go index 8fc9721f9..12bc886a8 100644 --- a/test/e2e/inspect_test.go +++ b/test/e2e/inspect_test.go @@ -466,4 +466,28 @@ var _ = Describe("Podman inspect", func() { Expect(len(inspect)).To(Equal(1)) Expect(len(inspect[0].NetworkSettings.Networks)).To(Equal(1)) }) + + It("Container inspect with unlimited uilimits should be -1", func() { + ctrName := "testctr" + session := podmanTest.Podman([]string{"run", "-d", "--ulimit", "core=-1:-1", "--name", ctrName, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + + inspect := podmanTest.Podman([]string{"inspect", ctrName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(BeZero()) + + data := inspect.InspectContainerToJSON() + ulimits := data[0].HostConfig.Ulimits + Expect(len(ulimits)).To(BeNumerically(">", 0)) + found := false + for _, ulimit := range ulimits { + if ulimit.Name == "RLIMIT_CORE" { + found = true + Expect(ulimit.Soft).To(BeNumerically("==", -1)) + Expect(ulimit.Hard).To(BeNumerically("==", -1)) + } + } + Expect(found).To(BeTrue()) + }) }) diff --git a/test/e2e/rmi_test.go b/test/e2e/rmi_test.go index 1f40e4928..c1fbe0440 100644 --- a/test/e2e/rmi_test.go +++ b/test/e2e/rmi_test.go @@ -1,7 +1,9 @@ package integration import ( + "fmt" "os" + "sync" . "github.com/containers/podman/v2/test/utils" . "github.com/onsi/ginkgo" @@ -276,4 +278,32 @@ RUN find $LOCAL match, _ := session.ErrorGrepString("image name or ID must be specified") Expect(match).To(BeTrue()) }) + + It("podman image rm - concurrent with shared layers", func() { + // #6510 has shown a fairly simple reproducer to force storage + // errors during parallel image removal. Since it's subject to + // a race, we may not hit the condition a 100 percent of times + // but ocal reproducers hit it all the time. + + var wg sync.WaitGroup + + buildAndRemove := func(i int) { + defer GinkgoRecover() + defer wg.Done() + imageName := fmt.Sprintf("rmtest:%d", i) + containerfile := `FROM quay.io/libpod/cirros:latest +RUN ` + fmt.Sprintf("touch %s", imageName) + + podmanTest.BuildImage(containerfile, imageName, "false") + session := podmanTest.Podman([]string{"rmi", "-f", imageName}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + } + + wg.Add(10) + for i := 0; i < 10; i++ { + go buildAndRemove(i) + } + wg.Wait() + }) }) diff --git a/test/e2e/run_volume_test.go b/test/e2e/run_volume_test.go index bc89b59de..19d82c974 100644 --- a/test/e2e/run_volume_test.go +++ b/test/e2e/run_volume_test.go @@ -304,6 +304,24 @@ var _ = Describe("Podman run with volumes", func() { Expect(separateVolumeSession.OutputToString()).To(Equal(baselineOutput)) }) + It("podman named volume copyup symlink", func() { + imgName := "testimg" + dockerfile := `FROM alpine +RUN touch /testfile +RUN sh -c "cd /etc/apk && ln -s ../../testfile"` + podmanTest.BuildImage(dockerfile, imgName, "false") + + baselineSession := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", imgName, "ls", "/etc/apk/"}) + baselineSession.WaitWithDefaultTimeout() + Expect(baselineSession.ExitCode()).To(Equal(0)) + baselineOutput := baselineSession.OutputToString() + + outputSession := podmanTest.Podman([]string{"run", "-t", "-i", "-v", "/etc/apk/", imgName, "ls", "/etc/apk/"}) + outputSession.WaitWithDefaultTimeout() + Expect(outputSession.ExitCode()).To(Equal(0)) + Expect(outputSession.OutputToString()).To(Equal(baselineOutput)) + }) + It("podman read-only tmpfs conflict with volume", func() { session := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", "--read-only", "-v", "tmp_volume:" + dest, ALPINE, "touch", dest + "/a"}) session.WaitWithDefaultTimeout() diff --git a/version/version.go b/version/version.go index 0bba0147b..8785dd9fd 100644 --- a/version/version.go +++ b/version/version.go @@ -8,7 +8,7 @@ import ( // NOTE: remember to bump the version at the top // of the top-level README.md file when this is // bumped. -var Version = semver.MustParse("3.0.0-dev") +var Version = semver.MustParse("3.0.1-dev") // APIVersion is the version for the remote // client API. It is used to determine compatibility |