diff options
131 files changed, 1734 insertions, 4109 deletions
diff --git a/DISTRO_PACKAGE.md b/DISTRO_PACKAGE.md new file mode 100644 index 000000000..133c62d23 --- /dev/null +++ b/DISTRO_PACKAGE.md @@ -0,0 +1,101 @@ +# Podman Packaging + +This document is currently written with Fedora as a reference, intended for use +by packagers of other distros as well as package users. + +## Fedora Users +Podman v4 is available as an official Fedora package on Fedora 36 and rawhide. +This version of Podman brings with it a new container stack called +Netavark which serves as a replacement for CNI plugins +(containernetworking-plugins on Fedora), as well as Aardvark-dns, the +authoritative DNS server for container records. + +Both Netavark and Aardvark-dns are available as official Fedora packages on +Fedora 35 and newer versions and form the default network stack for new +installations of Podman 4.0. + +On Fedora 36 and newer, fresh installations of Podman v4 will +automatically install Aardvark-dns along with Netavark. + +To install Podman v4: + +```console +$ sudo dnf install podman +``` + +To update Podman from an older version to v4: + +```console +$ sudo dnf update podman +``` + +**NOTE:** Fedora 35 users will not be able to install Podman v4 using the default yum +repositories and are recommended to use the COPR repo below: + +```console +$ sudo dnf copr enable rhcontainerbot/podman4 + +# install or update per your needs +$ sudo dnf install podman +``` + +After installation, if you would like to migrate all your containers to use +Netavark, you will need to set `network_backend = "netavark"` under +the `[network]` section in your containers.conf, typically located at: +`/usr/share/containers/containers.conf` + +### Testing the latest development version` + +If you would like to test the latest unreleased upstream code, try the +podman-next COPR + +```console +$ sudo dnf copr enable rhcontainerbot/podman-next + +$ sudo dnf install podman +``` + +**CAUTION:** The podman-next COPR provides the latest unreleased sources of Podman, +Netavark and Aardvark-dns as rpms which would override the versions provided by +the official packages. + +## Distro Packagers + +The Fedora packaging sources for Podman are available at the [Podman +dist-git](https://src.fedoraproject.org/rpms/podman). + +The main `podman` package no longer explicitly depends on +containernetworking-plugins. The network stack dependencies are now handled in +the [containers-common](https://src.fedoraproject.org/rpms/containers-common) +package which allows for a single point of dependency maintenance for Podman +and Buildah. + +- containers-common +``` +Requires: container-network-stack +Recommends: netavark +``` + +- netavark +``` +Provides: container-network-stack = 2 +``` + +- containernetworking-plugins +``` +Provides: container-network-stack = 1 +``` + +This configuration ensures: +- New installations of Podman will always install netavark by default. +- The containernetworking-plugins package will not conflict with netavark and +users can install them together. + +## Listing bundled dependencies +If you need to list the bundled dependencies in your packaging sources, you can +process the `go.mod` file in the upstream source. +For example, Fedora's packaging source uses: + +``` +$ awk '{print "Provides: bundled(golang("$1")) = "$2}' go.mod | sort | uniq | sed -e 's/-/_/g' -e '/bundled(golang())/d' -e '/bundled(golang(go\|module\|replace\|require))/d' +``` @@ -541,8 +541,8 @@ validate.completions: .PHONY: run-docker-py-tests run-docker-py-tests: touch test/__init__.py - env CONTAINERS_CONF=$(CURDIR)/test/apiv2/containers.conf pytest test/python/docker/ - -rm test/__init__.py + env CONTAINERS_CONF=$(CURDIR)/test/apiv2/containers.conf pytest --disable-warnings test/python/docker/ + rm -f test/__init__.py .PHONY: localunit localunit: test/goecho/goecho test/version/version @@ -623,9 +623,15 @@ remotesystem: .PHONY: localapiv2 localapiv2: - env PODMAN=./bin/podman ./test/apiv2/test-apiv2 - env CONTAINERS_CONF=$(CURDIR)/test/apiv2/containers.conf PODMAN=./bin/podman ${PYTHON} -m unittest discover -v ./test/apiv2/python - env CONTAINERS_CONF=$(CURDIR)/test/apiv2/containers.conf PODMAN=./bin/podman ${PYTHON} -m unittest discover -v ./test/python/docker + # Order is important running python tests first causes the bash tests to fail, see 12-imagesMore + # FIXME order of tests should not matter + env PODMAN=./bin/podman stdbuf -o0 -e0 ./test/apiv2/test-apiv2 + env CONTAINERS_CONF=$(CURDIR)/test/apiv2/containers.conf PODMAN=./bin/podman \ + pytest --disable-warnings ./test/apiv2/python + touch test/__init__.py + env CONTAINERS_CONF=$(CURDIR)/test/apiv2/containers.conf PODMAN=./bin/podman \ + pytest --disable-warnings ./test/python/docker + rm -f test/__init__.py .PHONY: remoteapiv2 remoteapiv2: @@ -949,5 +955,5 @@ clean: clean-binaries ## Clean all make artifacts libpod/pod_easyjson.go \ .install.goimports \ docs/build \ - venv + .venv make -C docs clean diff --git a/RELEASE_PROCESS.md b/RELEASE_PROCESS.md index e893d9e2e..3f63e5466 100644 --- a/RELEASE_PROCESS.md +++ b/RELEASE_PROCESS.md @@ -236,18 +236,22 @@ spelled with complete minutiae. 1. Return to the Cirrus-CI Build page for the new release tag, confirm (or wait for) it to complete, re-running any failed tasks as appropriate. - 1. For anything other than an RC, download the new release artifacts from CI - (the binaries which were actually tested). The items are - located under the *checks* tab in github for: - - * `Cirrus CI / Alt Arch. Cross` - tarball for each architecture - * `Cirrus CI / OSX Cross` - two zip files (amd64 and arm64) - * `Cirrus CI / Windows Cross` - an `msi` file - - Under the "Artifacts" section of each task, click the "gosrc" link, - find and download the release archive (`zip`, `tar.gz` or `.msi`). - Save the the archive with a meaningful name, for example - `podman-v3.0.0.msi`. + 1. For anything other than an RC, the release artifacts need to be published along + with the release. These can be built locally using: + + ```shell + $ git checkout vX.Y.Z + $ make podman-remote-release-darwin_amd64.zip \ + podman-remote-release-darwin_arm64.zip \ + podman-remote-release-windows_amd64.zip \ + podman.msi \ + podman-remote-static + $ mv podman-* bin/ + $ cd bin/ + $ tar -cvzf podman-remote-static.tar.gz podman-remote-static + $ sha256sum *.zip *.msi *.tar.gz > shasums + ``` + 1. The `podman-vX.Y.Z.dmg` file is produced manually by someone in possession of a developer signing key. 1. In the directory where you downloaded the archives, run @@ -263,8 +267,10 @@ spelled with complete minutiae. that to upload the artifacts you previously downloaded, including the `shasums` file. - * podman-remote-release-darwin.zip - * podman-remote-release-windows.zip + * podman-remote-release-darwin_amd64.zip + * podman-remote-release-darwin_arm64.zip + * podman-remote-release-windows_amd64.zip * podman-vX.Y.Z.msi + * podman-remote-static.tar.gz * shasums 1. Save the release. diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index a4f94616c..b110b3d85 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -117,7 +117,7 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *c case mount.TypeTmpfs: if m.TmpfsOptions != nil { addField(&builder, "tmpfs-size", strconv.FormatInt(m.TmpfsOptions.SizeBytes, 10)) - addField(&builder, "tmpfs-mode", strconv.FormatUint(uint64(m.TmpfsOptions.Mode), 10)) + addField(&builder, "tmpfs-mode", strconv.FormatUint(uint64(m.TmpfsOptions.Mode), 8)) } case mount.TypeVolume: // All current VolumeOpts are handled above diff --git a/cmd/podman/machine/rm.go b/cmd/podman/machine/rm.go index 38873084a..3e5d5fb0b 100644 --- a/cmd/podman/machine/rm.go +++ b/cmd/podman/machine/rm.go @@ -37,7 +37,7 @@ func init() { flags := rmCmd.Flags() formatFlagName := "force" - flags.BoolVar(&destoryOptions.Force, formatFlagName, false, "Do not prompt before rming") + flags.BoolVarP(&destoryOptions.Force, formatFlagName, "f", false, "Stop and do not prompt before rming") keysFlagName := "save-keys" flags.BoolVar(&destoryOptions.SaveKeys, keysFlagName, false, "Do not delete SSH keys") @@ -64,7 +64,7 @@ func rm(cmd *cobra.Command, args []string) error { if err != nil { return err } - confirmationMessage, remove, err := vm.Remove(vmName, machine.RemoveOptions{}) + confirmationMessage, remove, err := vm.Remove(vmName, destoryOptions) if err != nil { return err } diff --git a/contrib/cirrus/runner.sh b/contrib/cirrus/runner.sh index 3aa69183e..d1d87ad04 100755 --- a/contrib/cirrus/runner.sh +++ b/contrib/cirrus/runner.sh @@ -55,11 +55,8 @@ function _run_unit() { } function _run_apiv2() { - local m="Testing of API was performed using the **PODMAN*** client" - warn "$m" - if ! make localapiv2 |& logformatter; then - die "$m" - fi + source .venv/requests/bin/activate + make localapiv2 |& logformatter } function _run_compose() { @@ -100,12 +97,8 @@ function _run_bindings() { } function _run_docker-py() { - m="Testing of API was performed using the **DOCKER** client" - warn "$m" - source venv/bin/activate - if ! make run-docker-py-tests; then - die "$m" - fi + source .venv/docker-py/bin/activate + make run-docker-py-tests } function _run_endpoint() { diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh index 93f085983..696560166 100755 --- a/contrib/cirrus/setup_environment.sh +++ b/contrib/cirrus/setup_environment.sh @@ -265,14 +265,21 @@ case "$TEST_FLAVOR" in msg "Installing previously downloaded/cached packages" dnf install -y $PACKAGE_DOWNLOAD_DIR/python3*.rpm - virtualenv venv - source venv/bin/activate + virtualenv .venv/docker-py + source .venv/docker-py/bin/activate pip install --upgrade pip pip install --requirement $GOSRC/test/python/requirements.txt ;; build) make clean ;; unit) ;; - apiv2) ;& # use next item + apiv2) + msg "Installing previously downloaded/cached packages" + dnf install -y $PACKAGE_DOWNLOAD_DIR/python3*.rpm + virtualenv .venv/requests + source .venv/requests/bin/activate + pip install --upgrade pip + pip install --requirement $GOSRC/test/apiv2/python/requirements.txt + ;& # continue with next item compose) rpm -ivh $PACKAGE_DOWNLOAD_DIR/podman-docker* ;& # continue with next item diff --git a/docs/source/markdown/podman-machine-rm.1.md b/docs/source/markdown/podman-machine-rm.1.md index 7c9eb65c5..4eea1d2e4 100644 --- a/docs/source/markdown/podman-machine-rm.1.md +++ b/docs/source/markdown/podman-machine-rm.1.md @@ -23,9 +23,9 @@ is used. Print usage statement. -#### **--force** +#### **--force**, **-f** -Delete without confirmation +Stop and delete without confirmation #### **--save-ignition** @@ -58,6 +58,10 @@ The following files will be deleted: Are you sure you want to continue? [y/N] y ``` +``` +$ podman machine rm -f test1 +$ +``` ## SEE ALSO **[podman(1)](podman.1.md)**, **[podman-machine(1)](podman-machine.1.md)** @@ -14,16 +14,16 @@ require ( github.com/containers/buildah v1.24.2 github.com/containers/common v0.47.5-0.20220228211119-9880eb424fde github.com/containers/conmon v2.0.20+incompatible - github.com/containers/image/v5 v5.19.2-0.20220224100137-1045fb70b094 + github.com/containers/image/v5 v5.19.2-0.20220302121925-9a9cd9322006 github.com/containers/ocicrypt v1.1.2 github.com/containers/psgo v1.7.2 - github.com/containers/storage v1.38.3-0.20220214113600-26c561f9a645 + github.com/containers/storage v1.38.3-0.20220301151551-d06b0f81c0aa github.com/coreos/go-systemd/v22 v22.3.2 github.com/coreos/stream-metadata-go v0.0.0-20210225230131-70edb9eb47b3 github.com/cyphar/filepath-securejoin v0.2.3 github.com/davecgh/go-spew v1.1.1 github.com/digitalocean/go-qemu v0.0.0-20210326154740-ac9e0b687001 - github.com/docker/distribution v2.8.0+incompatible + github.com/docker/distribution v2.8.1+incompatible github.com/docker/docker v20.10.12+incompatible github.com/docker/go-connections v0.4.1-0.20210727194412-58542c764a11 github.com/docker/go-plugins-helpers v0.0.0-20211224144127-6eecb7beb651 @@ -61,7 +61,7 @@ require ( github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 github.com/uber/jaeger-client-go v2.30.0+incompatible github.com/ulikunitz/xz v0.5.10 - github.com/vbauerster/mpb/v6 v6.0.4 + github.com/vbauerster/mpb/v7 v7.3.2 github.com/vishvananda/netlink v1.1.1-0.20220115184804-dd687eb2f2d4 go.etcd.io/bbolt v1.3.6 golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce @@ -69,7 +69,7 @@ require ( golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 golang.org/x/text v0.3.7 google.golang.org/protobuf v1.27.1 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b + gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.22.5 k8s.io/apimachinery v0.22.5 ) @@ -100,8 +100,9 @@ github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JP github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= @@ -321,8 +322,9 @@ github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3 github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= github.com/containerd/stargz-snapshotter/estargz v0.9.0/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= github.com/containerd/stargz-snapshotter/estargz v0.10.1/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= -github.com/containerd/stargz-snapshotter/estargz v0.11.0 h1:t0IW5kOmY7AXDAWRUs2uVzDhijAUOAYVr/dyRhOQvBg= github.com/containerd/stargz-snapshotter/estargz v0.11.0/go.mod h1:/KsZXsJRllMbTKFfG0miFQWViQKdI9+9aSXs+HN0+ac= +github.com/containerd/stargz-snapshotter/estargz v0.11.1 h1:mNQqxcAWmDrV6d6yUvzFhfY8puNzoQz9v4diW+Pmei4= +github.com/containerd/stargz-snapshotter/estargz v0.11.1/go.mod h1:6VoPcf4M1wvnogWxqc4TqBWWErCS+R+ucnPZId2VbpQ= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= @@ -356,8 +358,9 @@ github.com/containers/common v0.47.5-0.20220228211119-9880eb424fde/go.mod h1:pks 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.19.1/go.mod h1:ewoo3u+TpJvGmsz64XgzbyTHwHtM94q7mgK/pX+v2SE= -github.com/containers/image/v5 v5.19.2-0.20220224100137-1045fb70b094 h1:27NmJhSA35ldAiq0pV2cXjj6YERVrQU8ectvWqblFxE= github.com/containers/image/v5 v5.19.2-0.20220224100137-1045fb70b094/go.mod h1:XoYK6kE0dpazFNcuS+a8lra+QfbC6s8tzv+cUuCrZpE= +github.com/containers/image/v5 v5.19.2-0.20220302121925-9a9cd9322006 h1:84BWsbDrrkzKG2nj3bA+/RyBcjJbb3vEwT2rwqsEWsw= +github.com/containers/image/v5 v5.19.2-0.20220302121925-9a9cd9322006/go.mod h1:S3gHB0QSB4IdADVtDbFbvFDdDq5v4ENMyXIo+Qi4cm8= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/libtrust v0.0.0-20200511145503-9c3a6c22cd9a h1:spAGlqziZjCJL25C6F1zsQY05tfCKE9F5YwtEWWe6hU= github.com/containers/libtrust v0.0.0-20200511145503-9c3a6c22cd9a/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= @@ -371,8 +374,8 @@ github.com/containers/psgo v1.7.2/go.mod h1:SLpqxsPOHtTqRygjutCPXmeU2PoEFzV3gzJp github.com/containers/storage v1.37.0/go.mod h1:kqeJeS0b7DO2ZT1nVWs0XufrmPFbgV3c+Q/45RlH6r4= github.com/containers/storage v1.38.0/go.mod h1:lBzt28gAk5ADZuRtwdndRJyqX22vnRaXmlF+7ktfMYc= github.com/containers/storage v1.38.2/go.mod h1:INP0RPLHWBxx+pTsO5uiHlDUGHDFvWZPWprAbAlQWPQ= -github.com/containers/storage v1.38.3-0.20220214113600-26c561f9a645 h1:2zzCKvfSq9T9zHGVWBygnYMdkBBmEsqXFa9gvWT4AFk= -github.com/containers/storage v1.38.3-0.20220214113600-26c561f9a645/go.mod h1:ugJrnJvpb6LrUshIYF/9g9YP9D4VTCYP+wShSiuqxN4= +github.com/containers/storage v1.38.3-0.20220301151551-d06b0f81c0aa h1:rdJIWaQ7PwS2N0ZWgn6NXDp+8KtvfSmPTJ3S5i6fJr4= +github.com/containers/storage v1.38.3-0.20220301151551-d06b0f81c0aa/go.mod h1:LkkL34WRi4dI4jt9Cp+ImdZi/P5i36glSHimT5CP5zM= 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/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -433,8 +436,9 @@ github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.8.0+incompatible h1:l9EaZDICImO1ngI+uTifW+ZYvvz7fKISBAKpg+MbWbY= github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= +github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.3-0.20220208084023-a5c757555091+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.12+incompatible h1:CEeNmFM0QZIsJCZKMkZx0ZcahTiewkrgiwfYD+dfl1U= @@ -867,6 +871,7 @@ github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.14.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.14.3/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.14.4 h1:eijASRJcobkVtSt81Olfh7JX43osYLwy5krOJo6YEu4= github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= @@ -936,7 +941,6 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= @@ -1184,7 +1188,6 @@ github.com/quasilyte/go-ruleguard/dsl v0.3.10/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQ github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc= github.com/quasilyte/go-ruleguard/rules v0.0.0-20210428214800-545e0d2e0bf7/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= @@ -1344,8 +1347,6 @@ github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/V github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI= -github.com/vbauerster/mpb/v6 v6.0.4 h1:h6J5zM/2wimP5Hj00unQuV8qbo5EPcj6wbkCqgj7KcY= -github.com/vbauerster/mpb/v6 v6.0.4/go.mod h1:a/+JT57gqh6Du0Ay5jSR+uBMfXGdlR7VQlGP52fJxLM= github.com/vbauerster/mpb/v7 v7.3.2 h1:tCuxMy8G9cLdjb61b6wO7I1vRT/LyMEzRbr3xCC0JPU= github.com/vbauerster/mpb/v7 v7.3.2/go.mod h1:wfxIZcOJq/bG1/lAtfzMXcOiSvbqVi/5GX5WCSi+IsA= github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE= diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index cef9e2c04..1517a7df7 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -173,6 +173,57 @@ func (c *Container) prepare() error { return nil } +// isWorkDirSymlink returns true if resolved workdir is symlink or a chain of symlinks, +// and final resolved target is present either on volume, mount or inside of container +// otherwise it returns false. Following function is meant for internal use only and +// can change at any point of time. +func (c *Container) isWorkDirSymlink(resolvedPath string) bool { + // We cannot create workdir since explicit --workdir is + // set in config but workdir could also be a symlink. + // If its a symlink lets check if resolved link is present + // on the container or not. + + // If we can resolve symlink and resolved link is present on the container + // then return nil cause its a valid use-case. + + maxSymLinks := 0 + for { + // Linux only supports a chain of 40 links. + // Reference: https://github.com/torvalds/linux/blob/master/include/linux/namei.h#L13 + if maxSymLinks > 40 { + break + } + resolvedSymlink, err := os.Readlink(resolvedPath) + if err != nil { + // End sym-link resolution loop. + break + } + if resolvedSymlink != "" { + _, resolvedSymlinkWorkdir, err := c.resolvePath(c.state.Mountpoint, resolvedSymlink) + if isPathOnVolume(c, resolvedSymlinkWorkdir) || isPathOnBindMount(c, resolvedSymlinkWorkdir) { + // Resolved symlink exists on external volume or mount + return true + } + if err != nil { + // Could not resolve path so end sym-link resolution loop. + break + } + if resolvedSymlinkWorkdir != "" { + resolvedPath = resolvedSymlinkWorkdir + _, err := os.Stat(resolvedSymlinkWorkdir) + if err == nil { + // Symlink resolved successfully and resolved path exists on container, + // this is a valid use-case so return nil. + logrus.Debugf("Workdir is a symlink with target to %q and resolved symlink exists on container", resolvedSymlink) + return true + } + } + } + maxSymLinks++ + } + return false +} + // resolveWorkDir resolves the container's workdir and, depending on the // configuration, will create it, or error out if it does not exist. // Note that the container must be mounted before. @@ -205,6 +256,11 @@ func (c *Container) resolveWorkDir() error { // the path exists on the container. if err != nil { if os.IsNotExist(err) { + // If resolved Workdir path gets marked as a valid symlink, + // return nil cause this is valid use-case. + if c.isWorkDirSymlink(resolvedWorkdir) { + return nil + } return errors.Errorf("workdir %q does not exist on container %s", workdir, c.ID()) } // This might be a serious error (e.g., permission), so diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 29b9941fe..7fd80927b 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -30,6 +30,7 @@ import ( "github.com/containers/podman/v4/pkg/util" "github.com/containers/podman/v4/utils" "github.com/containers/storage/pkg/lockfile" + spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -990,8 +991,20 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e return nil, err } - // We can't do more if the network is down. if c.state.NetNS == nil { + if networkNSPath := c.joinedNetworkNSPath(); networkNSPath != "" { + if result, err := c.inspectJoinedNetworkNS(networkNSPath); err == nil { + if basicConfig, err := resultToBasicNetworkConfig(result); err == nil { + // fallback to dummy configuration + settings.InspectBasicNetworkConfig = basicConfig + return settings, nil + } + } + // do not propagate error inspecting a joined network ns + logrus.Errorf("Error inspecting network namespace: %s of container %s: %v", networkNSPath, c.ID(), err) + } + // We can't do more if the network is down. + // We still want to make dummy configurations for each CNI net // the container joined. if len(networks) > 0 { @@ -1065,11 +1078,84 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e return settings, nil } +func (c *Container) joinedNetworkNSPath() string { + for _, namespace := range c.config.Spec.Linux.Namespaces { + if namespace.Type == spec.NetworkNamespace { + return namespace.Path + } + } + return "" +} + +func (c *Container) inspectJoinedNetworkNS(networkns string) (q types.StatusBlock, retErr error) { + var result types.StatusBlock + err := ns.WithNetNSPath(networkns, func(_ ns.NetNS) error { + ifaces, err := net.Interfaces() + if err != nil { + return err + } + routes, err := netlink.RouteList(nil, netlink.FAMILY_ALL) + if err != nil { + return err + } + var gateway net.IP + for _, route := range routes { + // default gateway + if route.Dst == nil { + gateway = route.Gw + } + } + result.Interfaces = make(map[string]types.NetInterface) + for _, iface := range ifaces { + if iface.Flags&net.FlagLoopback != 0 { + continue + } + addrs, err := iface.Addrs() + if err != nil { + continue + } + if len(addrs) == 0 { + continue + } + subnets := make([]types.NetAddress, 0, len(addrs)) + for _, address := range addrs { + if ipnet, ok := address.(*net.IPNet); ok { + if ipnet.IP.IsLinkLocalMulticast() || ipnet.IP.IsLinkLocalUnicast() { + continue + } + subnet := types.NetAddress{ + IPNet: types.IPNet{ + IPNet: *ipnet, + }, + } + if ipnet.Contains(gateway) { + subnet.Gateway = gateway + } + subnets = append(subnets, subnet) + } + } + result.Interfaces[iface.Name] = types.NetInterface{ + Subnets: subnets, + MacAddress: types.HardwareAddr(iface.HardwareAddr), + } + } + return nil + }) + return result, err +} + // resultToBasicNetworkConfig produces an InspectBasicNetworkConfig from a CNI // result func resultToBasicNetworkConfig(result types.StatusBlock) (define.InspectBasicNetworkConfig, error) { config := define.InspectBasicNetworkConfig{} - for _, netInt := range result.Interfaces { + interfaceNames := make([]string, len(result.Interfaces)) + for interfaceName := range result.Interfaces { + interfaceNames = append(interfaceNames, interfaceName) + } + // ensure consistent inspect results by sorting + sort.Strings(interfaceNames) + for _, interfaceName := range interfaceNames { + netInt := result.Interfaces[interfaceName] for _, netAddress := range netInt.Subnets { size, _ := netAddress.IPNet.Mask.Size() if netAddress.IPNet.IP.To4() != nil { diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go index 155ad5c2d..230491c1a 100644 --- a/libpod/runtime_pod_linux.go +++ b/libpod/runtime_pod_linux.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package libpod @@ -59,50 +60,52 @@ func (r *Runtime) NewPod(ctx context.Context, p specgen.PodSpecGenerator, option pod.valid = true // Check Cgroup parent sanity, and set it if it was not set - switch r.config.Engine.CgroupManager { - case config.CgroupfsCgroupsManager: - canUseCgroup := !rootless.IsRootless() || isRootlessCgroupSet(pod.config.CgroupParent) - if canUseCgroup { + if r.config.Cgroups() != "disabled" { + switch r.config.Engine.CgroupManager { + case config.CgroupfsCgroupsManager: + canUseCgroup := !rootless.IsRootless() || isRootlessCgroupSet(pod.config.CgroupParent) + if canUseCgroup { + if pod.config.CgroupParent == "" { + pod.config.CgroupParent = CgroupfsDefaultCgroupParent + } else if strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") { + return nil, errors.Wrapf(define.ErrInvalidArg, "systemd slice received as cgroup parent when using cgroupfs") + } + // If we are set to use pod cgroups, set the cgroup parent that + // all containers in the pod will share + // No need to create it with cgroupfs - the first container to + // launch should do it for us + if pod.config.UsePodCgroup { + pod.state.CgroupPath = filepath.Join(pod.config.CgroupParent, pod.ID()) + if p.InfraContainerSpec != nil { + p.InfraContainerSpec.CgroupParent = pod.state.CgroupPath + } + } + } + case config.SystemdCgroupsManager: if pod.config.CgroupParent == "" { - pod.config.CgroupParent = CgroupfsDefaultCgroupParent - } else if strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") { - return nil, errors.Wrapf(define.ErrInvalidArg, "systemd slice received as cgroup parent when using cgroupfs") + if rootless.IsRootless() { + pod.config.CgroupParent = SystemdDefaultRootlessCgroupParent + } else { + pod.config.CgroupParent = SystemdDefaultCgroupParent + } + } else if len(pod.config.CgroupParent) < 6 || !strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") { + return nil, errors.Wrapf(define.ErrInvalidArg, "did not receive systemd slice as cgroup parent when using systemd to manage cgroups") } // If we are set to use pod cgroups, set the cgroup parent that // all containers in the pod will share - // No need to create it with cgroupfs - the first container to - // launch should do it for us if pod.config.UsePodCgroup { - pod.state.CgroupPath = filepath.Join(pod.config.CgroupParent, pod.ID()) + cgroupPath, err := systemdSliceFromPath(pod.config.CgroupParent, fmt.Sprintf("libpod_pod_%s", pod.ID())) + if err != nil { + return nil, errors.Wrapf(err, "unable to create pod cgroup for pod %s", pod.ID()) + } + pod.state.CgroupPath = cgroupPath if p.InfraContainerSpec != nil { p.InfraContainerSpec.CgroupParent = pod.state.CgroupPath } } + default: + return nil, errors.Wrapf(define.ErrInvalidArg, "unsupported Cgroup manager: %s - cannot validate cgroup parent", r.config.Engine.CgroupManager) } - case config.SystemdCgroupsManager: - if pod.config.CgroupParent == "" { - if rootless.IsRootless() { - pod.config.CgroupParent = SystemdDefaultRootlessCgroupParent - } else { - pod.config.CgroupParent = SystemdDefaultCgroupParent - } - } else if len(pod.config.CgroupParent) < 6 || !strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") { - return nil, errors.Wrapf(define.ErrInvalidArg, "did not receive systemd slice as cgroup parent when using systemd to manage cgroups") - } - // If we are set to use pod cgroups, set the cgroup parent that - // all containers in the pod will share - if pod.config.UsePodCgroup { - cgroupPath, err := systemdSliceFromPath(pod.config.CgroupParent, fmt.Sprintf("libpod_pod_%s", pod.ID())) - if err != nil { - return nil, errors.Wrapf(err, "unable to create pod cgroup for pod %s", pod.ID()) - } - pod.state.CgroupPath = cgroupPath - if p.InfraContainerSpec != nil { - p.InfraContainerSpec.CgroupParent = pod.state.CgroupPath - } - } - default: - return nil, errors.Wrapf(define.ErrInvalidArg, "unsupported Cgroup manager: %s - cannot validate cgroup parent", r.config.Engine.CgroupManager) } if pod.config.UsePodCgroup { diff --git a/pkg/api/handlers/compat/exec.go b/pkg/api/handlers/compat/exec.go index c6f7e0318..def16d1b5 100644 --- a/pkg/api/handlers/compat/exec.go +++ b/pkg/api/handlers/compat/exec.go @@ -73,7 +73,7 @@ func ExecCreateHandler(w http.ResponseWriter, r *http.Request) { // Run the exit command after 5 minutes, to mimic Docker's exec cleanup // behavior. - libpodConfig.ExitCommandDelay = 5 * 60 + libpodConfig.ExitCommandDelay = runtimeConfig.Engine.ExitCommandDelay sessID, err := ctr.ExecCreate(libpodConfig) if err != nil { diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 213d8ceb7..155b06105 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -28,7 +28,7 @@ import ( "github.com/ghodss/yaml" "github.com/pkg/errors" "github.com/sirupsen/logrus" - yamlv3 "gopkg.in/yaml.v3" + yamlv2 "gopkg.in/yaml.v2" v1apps "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" ) @@ -365,7 +365,13 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY } } + ctrNames := make(map[string]string) for _, initCtr := range podYAML.Spec.InitContainers { + // Error out if same name is used for more than one container + if _, ok := ctrNames[initCtr.Name]; ok { + return nil, errors.Errorf("the pod %q is invalid; duplicate container name %q detected", podName, initCtr.Name) + } + ctrNames[initCtr.Name] = "" // Init containers cannot have either of lifecycle, livenessProbe, readinessProbe, or startupProbe set if initCtr.Lifecycle != nil || initCtr.LivenessProbe != nil || initCtr.ReadinessProbe != nil || initCtr.StartupProbe != nil { return nil, errors.Errorf("cannot create an init container that has either of lifecycle, livenessProbe, readinessProbe, or startupProbe set") @@ -414,6 +420,11 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY } for _, container := range podYAML.Spec.Containers { if !strings.Contains("infra", container.Name) { + // Error out if the same name is used for more than one container + if _, ok := ctrNames[container.Name]; ok { + return nil, errors.Errorf("the pod %q is invalid; duplicate container name %q detected", podName, container.Name) + } + ctrNames[container.Name] = "" pulledImage, labels, err := ic.getImageAndLabelInfo(ctx, cwd, annotations, writer, container, options) if err != nil { return nil, err @@ -650,7 +661,7 @@ func readConfigMapFromFile(r io.Reader) (v1.ConfigMap, error) { func splitMultiDocYAML(yamlContent []byte) ([][]byte, error) { var documentList [][]byte - d := yamlv3.NewDecoder(bytes.NewReader(yamlContent)) + d := yamlv2.NewDecoder(bytes.NewReader(yamlContent)) for { var o interface{} // read individual document @@ -664,7 +675,7 @@ func splitMultiDocYAML(yamlContent []byte) ([][]byte, error) { if o != nil { // back to bytes - document, err := yamlv3.Marshal(o) + document, err := yamlv2.Marshal(o) if err != nil { return nil, errors.Wrapf(err, "individual doc yaml could not be marshalled") } diff --git a/pkg/machine/config.go b/pkg/machine/config.go index efb1eda15..b3b105150 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -1,3 +1,4 @@ +//go:build amd64 || arm64 // +build amd64 arm64 package machine @@ -28,6 +29,8 @@ type InitOptions struct { Username string ReExec bool Rootful bool + // The numberical userid of the user that called machine + UID string } type QemuMachineStatus = string diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go index 47b1836f0..b2dabb689 100644 --- a/pkg/machine/ignition.go +++ b/pkg/machine/ignition.go @@ -51,6 +51,7 @@ type DynamicIgnition struct { Name string Key string TimeZone string + UID int VMName string WritePath string } @@ -63,12 +64,13 @@ func NewIgnitionFile(ign DynamicIgnition) error { ignVersion := Ignition{ Version: "3.2.0", } - ignPassword := Passwd{ Users: []PasswdUser{ { Name: ign.Name, SSHAuthorizedKeys: []SSHAuthorizedKey{SSHAuthorizedKey(ign.Key)}, + // Set the UID of the core user inside the machine + UID: intToPtr(ign.UID), }, { Name: "root", @@ -289,9 +291,7 @@ func getDirs(usrName string) []Directory { } func getFiles(usrName string) []File { - var ( - files []File - ) + files := make([]File, 0) lingerExample := `[Unit] Description=A systemd user unit demo @@ -310,6 +310,7 @@ machine_enabled=true delegateConf := `[Service] Delegate=memory pids cpu io ` + subUID := `%s:100000:1000000` // Add a fake systemd service to get the user socket rolling files = append(files, File{ @@ -344,6 +345,25 @@ Delegate=memory pids cpu io }, }) + // Setup /etc/subuid and /etc/subgid + for _, sub := range []string{"/etc/subuid", "/etc/subgid"} { + files = append(files, File{ + Node: Node{ + Group: getNodeGrp("root"), + Path: sub, + User: getNodeUsr("root"), + Overwrite: boolToPtr(true), + }, + FileEmbedded1: FileEmbedded1{ + Append: nil, + Contents: Resource{ + Source: encodeDataURLPtr(fmt.Sprintf(subUID, usrName)), + }, + Mode: intToPtr(0744), + }, + }) + } + // Set delegate.conf so cpu,io subsystem is delegated to non-root users as well for cgroupv2 // by default files = append(files, File{ diff --git a/pkg/machine/pull.go b/pkg/machine/pull.go index 280b47f96..cf1e708b1 100644 --- a/pkg/machine/pull.go +++ b/pkg/machine/pull.go @@ -19,8 +19,8 @@ import ( "github.com/containers/storage/pkg/archive" "github.com/sirupsen/logrus" "github.com/ulikunitz/xz" - "github.com/vbauerster/mpb/v6" - "github.com/vbauerster/mpb/v6/decor" + "github.com/vbauerster/mpb/v7" + "github.com/vbauerster/mpb/v7/decor" ) // GenericDownload is used when a user provides a URL diff --git a/pkg/machine/qemu/config.go b/pkg/machine/qemu/config.go index c619b7dd4..b39334be0 100644 --- a/pkg/machine/qemu/config.go +++ b/pkg/machine/qemu/config.go @@ -1,8 +1,11 @@ +//go:build (amd64 && !windows) || (arm64 && !windows) // +build amd64,!windows arm64,!windows package qemu -import "time" +import ( + "time" +) type Provider struct{} @@ -35,6 +38,8 @@ type MachineVM struct { RemoteUsername string // Whether this machine should run in a rootful or rootless manner Rootful bool + // UID is the numerical id of the user that called machine + UID int } type Mount struct { diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 9beec2173..3b4548c17 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -88,11 +88,16 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) { vm.Memory = opts.Memory vm.DiskSize = opts.DiskSize - // Look up the executable - execPath, err := exec.LookPath(QemuCommand) + // Find the qemu executable + cfg, err := config.Default() + if err != nil { + return nil, err + } + execPath, err := cfg.FindHelperBinary(QemuCommand, true) if err != nil { return nil, err } + cmd := append([]string{execPath}) // Add memory cmd = append(cmd, []string{"-m", strconv.Itoa(int(vm.Memory))}...) @@ -245,12 +250,13 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { } } v.Mounts = mounts + v.UID = os.Getuid() // Add location of bootable image v.CmdLine = append(v.CmdLine, "-drive", "if=virtio,file="+v.ImagePath) // This kind of stinks but no other way around this r/n if len(opts.IgnitionPath) < 1 { - uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/user/1000/podman/podman.sock", strconv.Itoa(v.Port), v.RemoteUsername) + uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", fmt.Sprintf("/run/user/%d/podman/podman.sock", v.UID), strconv.Itoa(v.Port), v.RemoteUsername) uriRoot := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/podman/podman.sock", strconv.Itoa(v.Port), "root") identity := filepath.Join(sshDir, v.Name) @@ -296,7 +302,16 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { // only if the virtualdisk size is less than // the given disk size if opts.DiskSize<<(10*3) > originalDiskSize { - resize := exec.Command("qemu-img", []string{"resize", v.ImagePath, strconv.Itoa(int(opts.DiskSize)) + "G"}...) + // Find the qemu executable + cfg, err := config.Default() + if err != nil { + return false, err + } + resizePath, err := cfg.FindHelperBinary("qemu-img", true) + if err != nil { + return false, err + } + resize := exec.Command(resizePath, []string{"resize", v.ImagePath, strconv.Itoa(int(opts.DiskSize)) + "G"}...) resize.Stdout = os.Stdout resize.Stderr = os.Stderr if err := resize.Run(); err != nil { @@ -319,6 +334,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { VMName: v.Name, TimeZone: opts.TimeZone, WritePath: v.IgnitionFilePath, + UID: v.UID, } err = machine.NewIgnitionFile(ign) return err == nil, err @@ -459,7 +475,17 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { for _, mount := range v.Mounts { fmt.Printf("Mounting volume... %s:%s\n", mount.Source, mount.Target) // create mountpoint directory if it doesn't exist - err = v.SSH(name, machine.SSHOptions{Args: []string{"-q", "--", "sudo", "mkdir", "-p", mount.Target}}) + // because / is immutable, we have to monkey around with permissions + // if we dont mount in /home or /mnt + args := []string{"-q", "--"} + if !strings.HasPrefix(mount.Target, "/home") || !strings.HasPrefix(mount.Target, "/mnt") { + args = append(args, "sudo", "chattr", "-i", "/", ";") + } + args = append(args, "sudo", "mkdir", "-p", mount.Target) + if !strings.HasPrefix(mount.Target, "/home") || !strings.HasPrefix(mount.Target, "/mnt") { + args = append(args, ";", "sudo", "chattr", "+i", "/", ";") + } + err = v.SSH(name, machine.SSHOptions{Args: args}) if err != nil { return err } @@ -653,7 +679,7 @@ func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, fun if err != nil { return "", nil, err } - if running { + if running && !opts.Force { return "", nil, errors.Errorf("running vm %q cannot be destroyed", v.Name) } @@ -667,6 +693,11 @@ func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, fun if !opts.SaveImage { files = append(files, v.ImagePath) } + socketPath, err := v.getForwardSocketPath() + if err != nil { + logrus.Error(err) + } + files = append(files, socketPath) files = append(files, v.archRemovalFiles()...) if err := machine.RemoveConnection(v.Name); err != nil { @@ -790,7 +821,16 @@ func (v *MachineVM) SSH(name string, opts machine.SSHOptions) error { // executes qemu-image info to get the virtual disk size // of the diskimage func getDiskSize(path string) (uint64, error) { - diskInfo := exec.Command("qemu-img", "info", "--output", "json", path) + // Find the qemu executable + cfg, err := config.Default() + if err != nil { + return 0, err + } + qemuPathDir, err := cfg.FindHelperBinary("qemu-img", true) + if err != nil { + return 0, err + } + diskInfo := exec.Command(qemuPathDir, "info", "--output", "json", path) stdout, err := diskInfo.StdoutPipe() if err != nil { return 0, err @@ -952,7 +992,7 @@ func (v *MachineVM) setupAPIForwarding(cmd []string) ([]string, string, apiForwa return cmd, "", noForwarding } - destSock := "/run/user/1000/podman/podman.sock" + destSock := fmt.Sprintf("/run/user/%d/podman/podman.sock", v.UID) forwardUser := "core" if v.Rootful { @@ -1064,16 +1104,15 @@ func waitAndPingAPI(sock string) { func waitAPIAndPrintInfo(forwardState apiForwardingState, forwardSock string, rootFul bool, name string) { if forwardState != noForwarding { + suffix := "" + if name != machine.DefaultMachineName { + suffix = " " + name + } waitAndPingAPI(forwardSock) if !rootFul { fmt.Printf("\nThis machine is currently configured in rootless mode. If your containers\n") fmt.Printf("require root permissions (e.g. ports < 1024), or if you run into compatibility\n") fmt.Printf("issues with non-podman clients, you can switch using the following command: \n") - - suffix := "" - if name != machine.DefaultMachineName { - suffix = " " + name - } fmt.Printf("\n\tpodman machine set --rootful%s\n\n", suffix) } @@ -1087,8 +1126,9 @@ func waitAPIAndPrintInfo(forwardState apiForwardingState, forwardSock string, ro fmt.Printf("\nThe system helper service is not installed; the default Docker API socket\n") fmt.Printf("address can't be used by podman. ") if helper := findClaimHelper(); len(helper) > 0 { - fmt.Printf("If you would like to install it run the\nfollowing command:\n") - fmt.Printf("\n\tsudo %s install\n\n", helper) + fmt.Printf("If you would like to install it run the\nfollowing commands:\n") + fmt.Printf("\n\tsudo %s install\n", helper) + fmt.Printf("\tpodman machine stop%s; podman machine start%s\n\n", suffix, suffix) } case machineLocal: fmt.Printf("\nAnother process was listening on the default Docker API socket address.\n") diff --git a/pkg/machine/qemu/options_darwin_arm64.go b/pkg/machine/qemu/options_darwin_arm64.go index 727a275d2..5b6cdc86d 100644 --- a/pkg/machine/qemu/options_darwin_arm64.go +++ b/pkg/machine/qemu/options_darwin_arm64.go @@ -45,6 +45,7 @@ func getOvmfDir(imagePath, vmName string) string { */ func getEdk2CodeFd(name string) string { dirs := []string{ + "/opt/homebrew/opt/podman/libexec/share/qemu", "/usr/local/share/qemu", "/opt/homebrew/share/qemu", } diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 8b3550e36..1cc3a463f 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -332,6 +332,11 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt } // Devices + // set the default rule at the beginning of device configuration + if !inUserNS && !s.Privileged { + g.AddLinuxResourcesDevice(false, "", nil, nil, "rwm") + } + var userDevices []spec.LinuxDevice if s.Privileged { // If privileged, we need to add all the host devices to the @@ -363,7 +368,6 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt // set the devices cgroup when not running in a user namespace if !inUserNS && !s.Privileged { - g.AddLinuxResourcesDevice(false, "", nil, nil, "rwm") for _, dev := range s.DeviceCgroupRule { g.AddLinuxResourcesDevice(true, dev.Type, dev.Major, dev.Minor, dev.Access) } diff --git a/pkg/util/utils.go b/pkg/util/utils.go index bdd1e1383..925ff9830 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -463,6 +463,8 @@ func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []strin var ( rootlessConfigHomeDirOnce sync.Once rootlessConfigHomeDir string + rootlessRuntimeDirOnce sync.Once + rootlessRuntimeDir string ) type tomlOptionsConfig struct { diff --git a/pkg/util/utils_supported.go b/pkg/util/utils_supported.go index e9d6bfa31..848b35a45 100644 --- a/pkg/util/utils_supported.go +++ b/pkg/util/utils_supported.go @@ -6,21 +6,67 @@ package util // should work to take darwin from this import ( + "fmt" "os" "path/filepath" "syscall" - cutil "github.com/containers/common/pkg/util" "github.com/containers/podman/v4/pkg/rootless" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) // GetRuntimeDir returns the runtime directory func GetRuntimeDir() (string, error) { + var rootlessRuntimeDirError error + if !rootless.IsRootless() { return "", nil } - return cutil.GetRuntimeDir() + + rootlessRuntimeDirOnce.Do(func() { + runtimeDir := os.Getenv("XDG_RUNTIME_DIR") + uid := fmt.Sprintf("%d", rootless.GetRootlessUID()) + if runtimeDir == "" { + tmpDir := filepath.Join("/run", "user", uid) + if err := os.MkdirAll(tmpDir, 0700); err != nil { + logrus.Debug(err) + } + st, err := os.Stat(tmpDir) + if err == nil && int(st.Sys().(*syscall.Stat_t).Uid) == os.Geteuid() && (st.Mode().Perm()&0700 == 0700) { + runtimeDir = tmpDir + } + } + if runtimeDir == "" { + tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("podman-run-%s", uid)) + if err := os.MkdirAll(tmpDir, 0700); err != nil { + logrus.Debug(err) + } + st, err := os.Stat(tmpDir) + if err == nil && int(st.Sys().(*syscall.Stat_t).Uid) == os.Geteuid() && (st.Mode().Perm()&0700 == 0700) { + runtimeDir = tmpDir + } + } + if runtimeDir == "" { + home := os.Getenv("HOME") + if home == "" { + rootlessRuntimeDirError = fmt.Errorf("neither XDG_RUNTIME_DIR nor HOME was set non-empty") + return + } + resolvedHome, err := filepath.EvalSymlinks(home) + if err != nil { + rootlessRuntimeDirError = errors.Wrapf(err, "cannot resolve %s", home) + return + } + runtimeDir = filepath.Join(resolvedHome, "rundir") + } + rootlessRuntimeDir = runtimeDir + }) + + if rootlessRuntimeDirError != nil { + return "", rootlessRuntimeDirError + } + return rootlessRuntimeDir, nil } // GetRootlessConfigHomeDir returns the config home directory when running as non root diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..5a31279c6 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +[tool.black] +line-length = 100 +target-version = ['py36'] +include = '\.pyi?$' +exclude = ''' +/( + \.git + | \.tox + | \.venv + | build + | dist + | docs + | hack +)/ +''' diff --git a/test/apiv2/12-imagesMore.at b/test/apiv2/12-imagesMore.at index 96eba1db5..67b4f1c79 100644 --- a/test/apiv2/12-imagesMore.at +++ b/test/apiv2/12-imagesMore.at @@ -26,7 +26,8 @@ t GET libpod/images/$IMAGE/json 200 \ .RepoTags[1]=localhost:5000/myrepo:mytag # Run registry container -podman run -d --name registry -p 5000:5000 quay.io/libpod/registry:2.6 /entrypoint.sh /etc/docker/registry/config.yml +# FIXME this fails if python tests have been run first... +podman run -d --name registry -p 5000:5000 quay.io/libpod/registry:2.7 /entrypoint.sh /etc/docker/registry/config.yml wait_for_port localhost 5000 # Push to local registry and check output @@ -58,7 +59,7 @@ t DELETE libpod/containers/registry?force=true 200 # Remove images t DELETE libpod/images/$IMAGE 200 \ .ExitCode=0 -t DELETE libpod/images/quay.io/libpod/registry:2.6 200 \ +t DELETE libpod/images/quay.io/libpod/registry:2.7 200 \ .ExitCode=0 if [ -z "${GOT_DIGEST}" ] ; then diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index cc5eda88e..49f8fb3fc 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -394,7 +394,8 @@ t GET containers/$cid/json 200 \ .Config.Healthcheck.Retries=3 # compat api: Test for mount options support -payload='{"Mounts":[{"Type":"tmpfs","Target":"/mnt/scratch","TmpfsOptions":{"SizeBytes":1024,"Mode":755}}]}' +# Sigh, JSON can't handle octal. 0755(octal) = 493(decimal) +payload='{"Mounts":[{"Type":"tmpfs","Target":"/mnt/scratch","TmpfsOptions":{"SizeBytes":1024,"Mode":493}}]}' t POST containers/create Image=$IMAGE HostConfig="$payload" 201 .Id~[0-9a-f]\\{64\\} cid=$(jq -r '.Id' <<<"$output") t GET containers/$cid/json 200 \ diff --git a/test/apiv2/python/conftest.py b/test/apiv2/python/conftest.py new file mode 100644 index 000000000..54a267049 --- /dev/null +++ b/test/apiv2/python/conftest.py @@ -0,0 +1,8 @@ +""" +Configure pytest +""" + + +def pytest_report_header(config): + """Add header to report.""" + return "python client -- requests library" diff --git a/test/apiv2/python/requirements.txt b/test/apiv2/python/requirements.txt new file mode 100644 index 000000000..d9cfc687a --- /dev/null +++ b/test/apiv2/python/requirements.txt @@ -0,0 +1,5 @@ +requests-mock~=1.9.3 +requests~=2.20.0 +setuptools~=50.3.2 +python-dateutil~=2.8.1 +PyYAML~=5.4.1 diff --git a/test/apiv2/test-apiv2 b/test/apiv2/test-apiv2 index bd728e130..ff328cfc8 100755 --- a/test/apiv2/test-apiv2 +++ b/test/apiv2/test-apiv2 @@ -368,7 +368,7 @@ function start_service() { die "Cannot start service on non-localhost ($HOST)" fi - echo $WORKDIR + echo "rootdir: "$WORKDIR # Some tests use shortnames; force registry override to work around # docker.io throttling. # FIXME esm revisit pulling expected images re: shortnames caused tests to fail @@ -376,7 +376,7 @@ function start_service() { $PODMAN_BIN \ --root $WORKDIR/server_root --syslog=true \ system service \ - --time 15 \ + --time 0 \ tcp:127.0.0.1:$PORT \ &> $WORKDIR/server.log & service_pid=$! @@ -443,7 +443,7 @@ function start_registry() { -e REGISTRY_HTTP_TLS_KEY=/auth/domain.key \ ${REGISTRY_IMAGE} - wait_for_port localhost $REGISTRY_PORT + wait_for_port localhost $REGISTRY_PORT 10 } function stop_registry() { @@ -492,13 +492,16 @@ function wait_for_port() { local port=$2 # Numeric port local _timeout=${3:-5} # Optional; default to 5 seconds + local path=/dev/tcp/$host/$port + # Wait - while [ $_timeout -gt 0 ]; do + local i=$_timeout + while [ $i -gt 0 ]; do { exec 3<> /dev/tcp/$host/$port; } &>/dev/null && return sleep 1 - _timeout=$(( $_timeout - 1 )) + i=$(( $i - 1 )) done - die "Timed out waiting for service" + die "Timed out (${_timeout}s) waiting for service ($path)" } ############ @@ -543,6 +546,9 @@ done ############################################################################### # BEGIN entry handler (subtest invoker) +echo '============================= test session starts ==============================' +echo "podman client -- $(curl --version)" + # Identify the tests to run. If called with args, use those as globs. tests_to_run=() if [ -n "$*" ]; then @@ -558,6 +564,7 @@ if [ -n "$*" ]; then else tests_to_run=($TESTS_DIR/*.at) fi +echo -e "collected ${#tests_to_run[@]} items\n" start_service diff --git a/test/e2e/build/Dockerfile.with-multiple-secret b/test/e2e/build/Containerfile.with-multiple-secret index f3478914f..f3478914f 100644 --- a/test/e2e/build/Dockerfile.with-multiple-secret +++ b/test/e2e/build/Containerfile.with-multiple-secret diff --git a/test/e2e/build/Dockerfile.with-secret b/test/e2e/build/Containerfile.with-secret index 920663a92..920663a92 100644 --- a/test/e2e/build/Dockerfile.with-secret +++ b/test/e2e/build/Containerfile.with-secret diff --git a/test/e2e/build/Dockerfile.test-cp-root-dir b/test/e2e/build/Dockerfile.test-cp-root-dir deleted file mode 100644 index 9f7de7c32..000000000 --- a/test/e2e/build/Dockerfile.test-cp-root-dir +++ /dev/null @@ -1,2 +0,0 @@ -FROM scratch -COPY Dockerfile.test-cp-root-dir / diff --git a/test/e2e/build/Dockerfile.with-secret-verify-leak b/test/e2e/build/secret-verify-leak/Containerfile.with-secret-verify-leak index 0957ac6a6..0957ac6a6 100644 --- a/test/e2e/build/Dockerfile.with-secret-verify-leak +++ b/test/e2e/build/secret-verify-leak/Containerfile.with-secret-verify-leak diff --git a/test/e2e/build/workdir-symlink/Dockerfile b/test/e2e/build/workdir-symlink/Dockerfile new file mode 100644 index 000000000..abc9b47ee --- /dev/null +++ b/test/e2e/build/workdir-symlink/Dockerfile @@ -0,0 +1,5 @@ +FROM alpine +RUN mkdir /tmp/destination +RUN ln -s /tmp/destination /tmp/link +WORKDIR /tmp/link +CMD ["echo", "hello"] diff --git a/test/e2e/build_test.go b/test/e2e/build_test.go index a1c2f5e54..c5903f037 100644 --- a/test/e2e/build_test.go +++ b/test/e2e/build_test.go @@ -60,7 +60,7 @@ var _ = Describe("Podman build", func() { }) It("podman build with a secret from file", func() { - session := podmanTest.Podman([]string{"build", "-f", "build/Dockerfile.with-secret", "-t", "secret-test", "--secret", "id=mysecret,src=build/secret.txt", "build/"}) + session := podmanTest.Podman([]string{"build", "-f", "build/Containerfile.with-secret", "-t", "secret-test", "--secret", "id=mysecret,src=build/secret.txt", "build/"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("somesecret")) @@ -71,7 +71,7 @@ var _ = Describe("Podman build", func() { }) It("podman build with multiple secrets from files", func() { - session := podmanTest.Podman([]string{"build", "-f", "build/Dockerfile.with-multiple-secret", "-t", "multiple-secret-test", "--secret", "id=mysecret,src=build/secret.txt", "--secret", "id=mysecret2,src=build/anothersecret.txt", "build/"}) + session := podmanTest.Podman([]string{"build", "-f", "build/Containerfile.with-multiple-secret", "-t", "multiple-secret-test", "--secret", "id=mysecret,src=build/secret.txt", "--secret", "id=mysecret2,src=build/anothersecret.txt", "build/"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("somesecret")) @@ -83,7 +83,7 @@ var _ = Describe("Podman build", func() { }) It("podman build with a secret from file and verify if secret file is not leaked into image", func() { - session := podmanTest.Podman([]string{"build", "-f", "build/Dockerfile.with-secret-verify-leak", "-t", "secret-test-leak", "--secret", "id=mysecret,src=build/secret.txt", "build/"}) + session := podmanTest.Podman([]string{"build", "-f", "build/secret-verify-leak/Containerfile.with-secret-verify-leak", "-t", "secret-test-leak", "--secret", "id=mysecret,src=build/secret.txt", "build/"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("somesecret")) @@ -259,6 +259,19 @@ var _ = Describe("Podman build", func() { Expect(session.OutputToString()).NotTo(ContainSubstring("io.podman.annotations.seccomp")) }) + It("podman build where workdir is a symlink and run without creating new workdir", func() { + session := podmanTest.Podman([]string{ + "build", "-f", "build/workdir-symlink/Dockerfile", "-t", "test-symlink", + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"run", "--workdir", "/tmp/link", "test-symlink"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("hello")) + }) + It("podman build --http_proxy flag", func() { os.Setenv("http_proxy", "1.2.3.4") if IsRemote() { diff --git a/test/e2e/containers_conf_test.go b/test/e2e/containers_conf_test.go index bfed01854..09cd68042 100644 --- a/test/e2e/containers_conf_test.go +++ b/test/e2e/containers_conf_test.go @@ -562,6 +562,11 @@ var _ = Describe("Verify podman containers.conf usage", func() { inspect = podmanTest.Podman([]string{"inspect", "--format", "{{ .HostConfig.Cgroups }}", result.OutputToString()}) inspect.WaitWithDefaultTimeout() Expect(inspect.OutputToString()).To(Equal("disabled")) + + // Check we can also create a pod when cgroups=disabled + result = podmanTest.Podman([]string{"pod", "create"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) }) It("podman containers.conf runtime", func() { diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index c0c71652e..6a4083565 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -38,6 +38,21 @@ spec: hostname: unknown ` +var workdirSymlinkPodYaml = ` +apiVersion: v1 +kind: Pod +metadata: + labels: + app: test-symlink + name: test-symlink +spec: + containers: + - image: test-symlink + name: test-symlink + resources: {} + restartPolicy: Never +` + var podnameEqualsContainerNameYaml = ` apiVersion: v1 kind: Pod @@ -1332,6 +1347,26 @@ var _ = Describe("Podman play kube", func() { Expect(sharednamespaces).To(ContainSubstring("pid")) }) + It("podman play kube should be able to run image where workdir is a symlink", func() { + session := podmanTest.Podman([]string{ + "build", "-f", "build/workdir-symlink/Dockerfile", "-t", "test-symlink", + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + err := writeYaml(workdirSymlinkPodYaml, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + logs := podmanTest.Podman([]string{"pod", "logs", "-c", "test-symlink-test-symlink", "test-symlink"}) + logs.WaitWithDefaultTimeout() + Expect(logs).Should(Exit(0)) + Expect(logs.OutputToString()).To(ContainSubstring("hello")) + }) + It("podman play kube should not rename pod if container in pod has same name", func() { err := writeYaml(podnameEqualsContainerNameYaml, kubeYaml) Expect(err).To(BeNil()) @@ -1853,6 +1888,26 @@ var _ = Describe("Podman play kube", func() { Expect(kube).Should(Exit(0)) }) + It("podman play kube test duplicate container name", func() { + p := getPod(withCtr(getCtr(withName("testctr"), withCmd([]string{"echo", "hello"}))), withCtr(getCtr(withName("testctr"), withCmd([]string{"echo", "world"})))) + + err := generateKubeYaml("pod", p, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).To(ExitWithError()) + + p = getPod(withPodInitCtr(getCtr(withImage(ALPINE), withCmd([]string{"echo", "hello"}), withInitCtr(), withName("initctr"))), withCtr(getCtr(withImage(ALPINE), withName("initctr"), withCmd([]string{"top"})))) + + err = generateKubeYaml("pod", p, kubeYaml) + Expect(err).To(BeNil()) + + kube = podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).To(ExitWithError()) + }) + It("podman play kube test hostname", func() { pod := getPod() err := generateKubeYaml("pod", pod, kubeYaml) diff --git a/test/e2e/run_device_test.go b/test/e2e/run_device_test.go index b8bdc84f8..479837dda 100644 --- a/test/e2e/run_device_test.go +++ b/test/e2e/run_device_test.go @@ -44,6 +44,11 @@ var _ = Describe("Podman run device", func() { session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg", ALPINE, "test", "-c", "/dev/kmsg"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) + if !isRootless() { + session = podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg", "--cap-add", "SYS_ADMIN", ALPINE, "head", "-n", "1", "/dev/kmsg"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + } }) It("podman run device rename test", func() { diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index aa1887f84..2202cadd8 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -2,15 +2,19 @@ package integration import ( "fmt" + "net" "os" "strings" + "syscall" + "github.com/containernetworking/plugins/pkg/ns" . "github.com/containers/podman/v4/test/utils" "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gexec" "github.com/uber/jaeger-client-go/utils" + "github.com/vishvananda/netlink" ) var _ = Describe("Podman run networking", func() { @@ -694,6 +698,157 @@ EXPOSE 2004-2005/tcp`, ALPINE) Expect(session.OutputToString()).To(ContainSubstring("11.11.11.11")) }) + addAddr := func(cidr string, containerInterface netlink.Link) error { + _, ipnet, err := net.ParseCIDR(cidr) + Expect(err).To(BeNil()) + addr := &netlink.Addr{IPNet: ipnet, Label: ""} + if err := netlink.AddrAdd(containerInterface, addr); err != nil && err != syscall.EEXIST { + return err + } + return nil + } + + loopbackup := func() { + lo, err := netlink.LinkByName("lo") + Expect(err).To(BeNil()) + err = netlink.LinkSetUp(lo) + Expect(err).To(BeNil()) + } + + linkup := func(name string, mac string, addresses []string) { + linkAttr := netlink.NewLinkAttrs() + linkAttr.Name = name + m, err := net.ParseMAC(mac) + Expect(err).To(BeNil()) + linkAttr.HardwareAddr = net.HardwareAddr(m) + eth := &netlink.Dummy{LinkAttrs: linkAttr} + err = netlink.LinkAdd(eth) + Expect(err).To(BeNil()) + err = netlink.LinkSetUp(eth) + Expect(err).To(BeNil()) + for _, address := range addresses { + err := addAddr(address, eth) + Expect(err).To(BeNil()) + } + } + + routeAdd := func(gateway string) { + gw := net.ParseIP(gateway) + route := &netlink.Route{Dst: nil, Gw: gw} + netlink.RouteAdd(route) + } + + setupNetworkNs := func(networkNSName string) { + ns.WithNetNSPath("/run/netns/"+networkNSName, func(_ ns.NetNS) error { + loopbackup() + linkup("eth0", "46:7f:45:6e:4f:c8", []string{"10.25.40.0/24", "fd04:3e42:4a4e:3381::/64"}) + linkup("eth1", "56:6e:35:5d:3e:a8", []string{"10.88.0.0/16"}) + + routeAdd("10.25.40.0") + return nil + }) + } + + checkNetworkNsInspect := func(name string) { + inspectOut := podmanTest.InspectContainer(name) + Expect(inspectOut[0].NetworkSettings.IPAddress).To(Equal("10.25.40.0")) + Expect(inspectOut[0].NetworkSettings.IPPrefixLen).To(Equal(24)) + Expect(len(inspectOut[0].NetworkSettings.SecondaryIPAddresses)).To(Equal(1)) + Expect(inspectOut[0].NetworkSettings.SecondaryIPAddresses[0].Addr).To(Equal("10.88.0.0")) + Expect(inspectOut[0].NetworkSettings.SecondaryIPAddresses[0].PrefixLength).To(Equal(16)) + Expect(inspectOut[0].NetworkSettings.GlobalIPv6Address).To(Equal("fd04:3e42:4a4e:3381::")) + Expect(inspectOut[0].NetworkSettings.GlobalIPv6PrefixLen).To(Equal(64)) + Expect(len(inspectOut[0].NetworkSettings.SecondaryIPv6Addresses)).To(Equal(0)) + Expect(inspectOut[0].NetworkSettings.MacAddress).To(Equal("46:7f:45:6e:4f:c8")) + Expect(len(inspectOut[0].NetworkSettings.AdditionalMacAddresses)).To(Equal(1)) + Expect(inspectOut[0].NetworkSettings.AdditionalMacAddresses[0]).To(Equal("56:6e:35:5d:3e:a8")) + Expect(inspectOut[0].NetworkSettings.Gateway).To(Equal("10.25.40.0")) + + } + + It("podman run newtork inspect fails gracefully on non-reachable network ns", func() { + SkipIfRootless("ip netns is not supported for rootless users") + + networkNSName := RandomString(12) + addNamedNetwork := SystemExec("ip", []string{"netns", "add", networkNSName}) + Expect(addNamedNetwork).Should(Exit(0)) + + setupNetworkNs(networkNSName) + + name := RandomString(12) + session := podmanTest.Podman([]string{"run", "-d", "--name", name, "--net", "ns:/run/netns/" + networkNSName, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + + // delete the named network ns before inspect + delNetworkNamespace := SystemExec("ip", []string{"netns", "delete", networkNSName}) + Expect(delNetworkNamespace).Should(Exit(0)) + + inspectOut := podmanTest.InspectContainer(name) + Expect(inspectOut[0].NetworkSettings.IPAddress).To(Equal("")) + Expect(len(inspectOut[0].NetworkSettings.Networks)).To(Equal(0)) + }) + + It("podman inspect can handle joined network ns with multiple interfaces", func() { + SkipIfRootless("ip netns is not supported for rootless users") + + networkNSName := RandomString(12) + addNamedNetwork := SystemExec("ip", []string{"netns", "add", networkNSName}) + Expect(addNamedNetwork).Should(Exit(0)) + defer func() { + delNetworkNamespace := SystemExec("ip", []string{"netns", "delete", networkNSName}) + Expect(delNetworkNamespace).Should(Exit(0)) + }() + setupNetworkNs(networkNSName) + + name := RandomString(12) + session := podmanTest.Podman([]string{"run", "--name", name, "--net", "ns:/run/netns/" + networkNSName, ALPINE}) + session.WaitWithDefaultTimeout() + + session = podmanTest.Podman([]string{"container", "rm", name}) + session.WaitWithDefaultTimeout() + + // no network teardown should touch joined network ns interfaces + session = podmanTest.Podman([]string{"run", "-d", "--replace", "--name", name, "--net", "ns:/run/netns/" + networkNSName, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + + checkNetworkNsInspect(name) + }) + + It("podman do not tamper with joined network ns interfaces", func() { + SkipIfRootless("ip netns is not supported for rootless users") + + networkNSName := RandomString(12) + addNamedNetwork := SystemExec("ip", []string{"netns", "add", networkNSName}) + Expect(addNamedNetwork).Should(Exit(0)) + defer func() { + delNetworkNamespace := SystemExec("ip", []string{"netns", "delete", networkNSName}) + Expect(delNetworkNamespace).Should(Exit(0)) + }() + + setupNetworkNs(networkNSName) + + name := RandomString(12) + session := podmanTest.Podman([]string{"run", "--name", name, "--net", "ns:/run/netns/" + networkNSName, ALPINE}) + session.WaitWithDefaultTimeout() + + checkNetworkNsInspect(name) + + name = RandomString(12) + session = podmanTest.Podman([]string{"run", "--name", name, "--net", "ns:/run/netns/" + networkNSName, ALPINE}) + session.WaitWithDefaultTimeout() + + checkNetworkNsInspect(name) + + // delete container, the network inspect should not change + session = podmanTest.Podman([]string{"container", "rm", name}) + session.WaitWithDefaultTimeout() + + session = podmanTest.Podman([]string{"run", "-d", "--replace", "--name", name, "--net", "ns:/run/netns/" + networkNSName, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + + checkNetworkNsInspect(name) + }) + It("podman run network in bogus user created network namespace", func() { session := podmanTest.Podman([]string{"run", "-dt", "--net", "ns:/run/netns/xxy", ALPINE, "wget", "www.podman.io"}) session.Wait(90) diff --git a/test/e2e/system_service_test.go b/test/e2e/system_service_test.go index dcf5e03b2..2bc7756d6 100644 --- a/test/e2e/system_service_test.go +++ b/test/e2e/system_service_test.go @@ -58,6 +58,7 @@ var _ = Describe("podman system service", func() { const magicComment = "pprof service listening on" It("are available", func() { + Skip("FIXME: Test is too flaky (#12624)") SkipIfRemote("service subcommand not supported remotely") address := url.URL{ @@ -97,6 +98,7 @@ var _ = Describe("podman system service", func() { }) It("are not available", func() { + Skip("FIXME: Test is too flaky (#12624)") SkipIfRemote("service subcommand not supported remotely") address := url.URL{ diff --git a/test/python/docker/__init__.py b/test/python/docker/__init__.py index 816667b82..d46f465f7 100644 --- a/test/python/docker/__init__.py +++ b/test/python/docker/__init__.py @@ -1,4 +1,6 @@ -import configparser +""" +Helpers for integration tests using DockerClient +""" import json import os import pathlib @@ -11,7 +13,7 @@ from docker import DockerClient from .compat import constant -class Podman(object): +class PodmanAPI: """ Instances hold the configuration and setup for running podman commands """ @@ -53,17 +55,13 @@ location = "mirror.localhost:5000" """ - with open(os.environ["CONTAINERS_REGISTRIES_CONF"], "w") as w: - w.write(conf) + with open(os.environ["CONTAINERS_REGISTRIES_CONF"], "w") as file: + file.write(conf) - os.environ["CNI_CONFIG_PATH"] = os.path.join( - self.anchor_directory, "cni", "net.d" - ) + os.environ["CNI_CONFIG_PATH"] = os.path.join(self.anchor_directory, "cni", "net.d") os.makedirs(os.environ["CNI_CONFIG_PATH"], exist_ok=True) self.cmd.append("--network-config-dir=" + os.environ["CNI_CONFIG_PATH"]) - cni_cfg = os.path.join( - os.environ["CNI_CONFIG_PATH"], "87-podman-bridge.conflist" - ) + cni_cfg = os.path.join(os.environ["CNI_CONFIG_PATH"], "87-podman-bridge.conflist") # json decoded and encoded to ensure legal json buf = json.loads( """ @@ -93,8 +91,8 @@ location = "mirror.localhost:5000" } """ ) - with open(cni_cfg, "w") as w: - json.dump(buf, w) + with open(cni_cfg, "w") as file: + json.dump(buf, file) def open(self, command, *args, **kwargs): """Podman initialized instance to run a given command @@ -111,6 +109,7 @@ location = "mirror.localhost:5000" shell = kwargs.get("shell", False) + # pylint: disable=consider-using-with return subprocess.Popen( cmd, shell=shell, @@ -144,9 +143,11 @@ location = "mirror.localhost:5000" ) def tear_down(self): + """Delete test environment.""" shutil.rmtree(self.anchor_directory, ignore_errors=True) def restore_image_from_cache(self, client: DockerClient): + """Populate images from cache.""" path = os.path.join(self.image_cache, constant.ALPINE_TARBALL) if not os.path.exists(path): img = client.images.pull(constant.ALPINE) @@ -157,5 +158,6 @@ location = "mirror.localhost:5000" self.run("load", "-i", path, check=True) def flush_image_cache(self): - for f in pathlib.Path(self.image_cache).glob("*.tar"): - f.unlink(f) + """Delete image cache.""" + for file in pathlib.Path(self.image_cache).glob("*.tar"): + file.unlink(missing_ok=True) diff --git a/test/python/docker/compat/common.py b/test/python/docker/compat/common.py index bdc67c287..218ac9872 100644 --- a/test/python/docker/compat/common.py +++ b/test/python/docker/compat/common.py @@ -1,23 +1,92 @@ +""" +Fixtures and Helpers for unittests. +""" +import subprocess +import sys +import time +import unittest + +# pylint: disable=no-name-in-module,import-error,wrong-import-order from docker import DockerClient +from test.python.docker import PodmanAPI from test.python.docker.compat import constant def run_top_container(client: DockerClient): - c = client.containers.create( - constant.ALPINE, command="top", detach=True, tty=True, name="top" + """Run top command in a alpine container.""" + ctnr = client.containers.create( + constant.ALPINE, + command="top", + detach=True, + tty=True, + name="top", ) - c.start() - return c.id + ctnr.start() + return ctnr.id def remove_all_containers(client: DockerClient): + """Delete all containers from the Podman service.""" for ctnr in client.containers.list(all=True): ctnr.remove(force=True) def remove_all_images(client: DockerClient): + """Delete all images from the Podman service.""" for img in client.images.list(): # FIXME should DELETE /images accept the sha256: prefix? id_ = img.id.removeprefix("sha256:") client.images.remove(id_, force=True) + + +class DockerTestCase(unittest.TestCase): + """Specialized TestCase class for testing against Podman service.""" + + podman: PodmanAPI = None # initialized podman configuration for tests + service: subprocess.Popen = None # podman service instance + + top_container_id: str = None + docker: DockerClient = None + + @classmethod + def setUpClass(cls) -> None: + super().setUpClass() + + cls.podman = PodmanAPI() + super().addClassCleanup(cls.podman.tear_down) + + cls.service = cls.podman.open("system", "service", "tcp:127.0.0.1:8080", "--time=0") + # give the service some time to be ready... + time.sleep(2) + + return_code = cls.service.poll() + if return_code is not None: + raise subprocess.CalledProcessError(return_code, "podman system service") + + @classmethod + def tearDownClass(cls) -> None: + cls.service.terminate() + stdout, stderr = cls.service.communicate(timeout=0.5) + if stdout: + sys.stdout.write("\ndocker-py -- Service Stdout:\n" + stdout.decode("utf-8")) + if stderr: + sys.stderr.write("\ndocker-py -- Service Stderr:\n" + stderr.decode("utf-8")) + + return super().tearDownClass() + + def setUp(self) -> None: + super().setUp() + + self.docker = DockerClient(base_url="tcp://127.0.0.1:8080", timeout=15) + self.addCleanup(self.docker.close) + + self.podman.restore_image_from_cache(self.docker) + self.top_container_id = run_top_container(self.docker) + self.assertIsNotNone(self.top_container_id, "Failed to create 'top' container") + + def tearDown(self) -> None: + remove_all_containers(self.docker) + remove_all_images(self.docker) + + super().tearDown() diff --git a/test/python/docker/compat/constant.py b/test/python/docker/compat/constant.py index 892293c97..2a0046daf 100644 --- a/test/python/docker/compat/constant.py +++ b/test/python/docker/compat/constant.py @@ -1,6 +1,8 @@ +""" +Constants to use in writing unittests. +""" ALPINE = "quay.io/libpod/alpine:latest" ALPINE_SHORTNAME = "alpine" ALPINE_TARBALL = "alpine.tar" BB = "quay.io/libpod/busybox:latest" NGINX = "quay.io/libpod/alpine_nginx:latest" -infra = "k8s.gcr.io/pause:3.2" diff --git a/test/python/docker/compat/test_containers.py b/test/python/docker/compat/test_containers.py index d14c09fc1..c0b6c9abe 100644 --- a/test/python/docker/compat/test_containers.py +++ b/test/python/docker/compat/test_containers.py @@ -1,111 +1,56 @@ +""" +Integration tests for exercising docker-py against Podman Service. +""" import io -import subprocess -import sys -import time -import unittest -from typing import IO, Optional, List +import tarfile +from typing import IO, List, Optional -from docker import DockerClient, errors +from docker import errors from docker.models.containers import Container from docker.models.images import Image from docker.models.volumes import Volume from docker.types import Mount -from test.python.docker import Podman +# pylint: disable=no-name-in-module,import-error,wrong-import-order from test.python.docker.compat import common, constant -import tarfile - - -class TestContainers(unittest.TestCase): - podman = None # initialized podman configuration for tests - service = None # podman service instance - topContainerId = "" - - def setUp(self): - super().setUp() - self.client = DockerClient(base_url="tcp://127.0.0.1:8080", timeout=15) - TestContainers.podman.restore_image_from_cache(self.client) - TestContainers.topContainerId = common.run_top_container(self.client) - self.assertIsNotNone(TestContainers.topContainerId) - - def tearDown(self): - common.remove_all_containers(self.client) - common.remove_all_images(self.client) - self.client.close() - return super().tearDown() - - @classmethod - def setUpClass(cls): - super().setUpClass() - TestContainers.podman = Podman() - TestContainers.service = TestContainers.podman.open( - "system", "service", "tcp:127.0.0.1:8080", "--time=0" - ) - # give the service some time to be ready... - time.sleep(2) - - rc = TestContainers.service.poll() - if rc is not None: - raise subprocess.CalledProcessError(rc, "podman system service") - @classmethod - def tearDownClass(cls): - TestContainers.service.terminate() - stdout, stderr = TestContainers.service.communicate(timeout=0.5) - if stdout: - sys.stdout.write("\nContainers Service Stdout:\n" + stdout.decode("utf-8")) - if stderr: - sys.stderr.write("\nContainers Service Stderr:\n" + stderr.decode("utf-8")) - - TestContainers.podman.tear_down() - return super().tearDownClass() +# pylint: disable=missing-function-docstring +class TestContainers(common.DockerTestCase): + """TestCase for exercising containers.""" def test_create_container(self): - # Run a container with detach mode - self.client.containers.create(image="alpine", detach=True) - self.assertEqual(len(self.client.containers.list(all=True)), 2) + """Run a container with detach mode.""" + self.docker.containers.create(image="alpine", detach=True) + self.assertEqual(len(self.docker.containers.list(all=True)), 2) def test_create_network(self): - net = self.client.networks.create("testNetwork", driver="bridge") - ctnr = self.client.containers.create(image="alpine", detach=True) - - # TODO fix when ready - # This test will not work until all connect|disconnect - # code is fixed. - # net.connect(ctnr) - - # nets = self.client.networks.list(greedy=True) - # self.assertGreaterEqual(len(nets), 1) - - # TODO fix endpoint to include containers - # for n in nets: - # if n.id == "testNetwork": - # self.assertEqual(ctnr.id, n.containers) - # self.assertTrue(False, "testNetwork not found") + """Add network to a container.""" + self.docker.networks.create("testNetwork", driver="bridge") + self.docker.containers.create(image="alpine", detach=True) def test_start_container(self): # Podman docs says it should give a 304 but returns with no response # # Start a already started container should return 304 - # response = self.client.api.start(container=TestContainers.topContainerId) + # response = self.docker.api.start(container=self.top_container_id) # self.assertEqual(error.exception.response.status_code, 304) # Create a new container and validate the count - self.client.containers.create(image=constant.ALPINE, name="container2") - containers = self.client.containers.list(all=True) + self.docker.containers.create(image=constant.ALPINE, name="container2") + containers = self.docker.containers.list(all=True) self.assertEqual(len(containers), 2) def test_start_container_with_random_port_bind(self): - container = self.client.containers.create( + container = self.docker.containers.create( image=constant.ALPINE, name="containerWithRandomBind", ports={"1234/tcp": None}, ) - containers = self.client.containers.list(all=True) + containers = self.docker.containers.list(all=True) self.assertTrue(container in containers) def test_stop_container(self): - top = self.client.containers.get(TestContainers.topContainerId) + top = self.docker.containers.get(self.top_container_id) self.assertEqual(top.status, "running") # Stop a running container and validate the state @@ -114,7 +59,7 @@ class TestContainers(unittest.TestCase): self.assertIn(top.status, ("stopped", "exited")) def test_kill_container(self): - top = self.client.containers.get(TestContainers.topContainerId) + top = self.docker.containers.get(self.top_container_id) self.assertEqual(top.status, "running") # Kill a running container and validate the state @@ -124,7 +69,7 @@ class TestContainers(unittest.TestCase): def test_restart_container(self): # Validate the container state - top = self.client.containers.get(TestContainers.topContainerId) + top = self.docker.containers.get(self.top_container_id) top.stop() top.reload() self.assertIn(top.status, ("stopped", "exited")) @@ -136,16 +81,16 @@ class TestContainers(unittest.TestCase): def test_remove_container(self): # Remove container by ID with force - top = self.client.containers.get(TestContainers.topContainerId) + top = self.docker.containers.get(self.top_container_id) top.remove(force=True) - self.assertEqual(len(self.client.containers.list()), 0) + self.assertEqual(len(self.docker.containers.list()), 0) def test_remove_container_without_force(self): # Validate current container count - self.assertEqual(len(self.client.containers.list()), 1) + self.assertEqual(len(self.docker.containers.list()), 1) # Remove running container should throw error - top = self.client.containers.get(TestContainers.topContainerId) + top = self.docker.containers.get(self.top_container_id) with self.assertRaises(errors.APIError) as error: top.remove() self.assertEqual(error.exception.response.status_code, 500) @@ -153,11 +98,11 @@ class TestContainers(unittest.TestCase): # Remove container by ID without force top.stop() top.remove() - self.assertEqual(len(self.client.containers.list()), 0) + self.assertEqual(len(self.docker.containers.list()), 0) def test_pause_container(self): # Validate the container state - top = self.client.containers.get(TestContainers.topContainerId) + top = self.docker.containers.get(self.top_container_id) self.assertEqual(top.status, "running") # Pause a running container and validate the state @@ -167,7 +112,7 @@ class TestContainers(unittest.TestCase): def test_pause_stopped_container(self): # Stop the container - top = self.client.containers.get(TestContainers.topContainerId) + top = self.docker.containers.get(self.top_container_id) top.stop() # Pause exited container should throw error @@ -176,7 +121,7 @@ class TestContainers(unittest.TestCase): self.assertEqual(error.exception.response.status_code, 500) def test_unpause_container(self): - top = self.client.containers.get(TestContainers.topContainerId) + top = self.docker.containers.get(self.top_container_id) # Validate the container state top.pause() @@ -190,21 +135,21 @@ class TestContainers(unittest.TestCase): def test_list_container(self): # Add container and validate the count - self.client.containers.create(image="alpine", detach=True) - containers = self.client.containers.list(all=True) + self.docker.containers.create(image="alpine", detach=True) + containers = self.docker.containers.list(all=True) self.assertEqual(len(containers), 2) def test_filters(self): self.skipTest("TODO Endpoint does not yet support filters") # List container with filter by id - filters = {"id": TestContainers.topContainerId} - ctnrs = self.client.containers.list(all=True, filters=filters) + filters = {"id": self.top_container_id} + ctnrs = self.docker.containers.list(all=True, filters=filters) self.assertEqual(len(ctnrs), 1) # List container with filter by name filters = {"name": "top"} - ctnrs = self.client.containers.list(all=True, filters=filters) + ctnrs = self.docker.containers.list(all=True, filters=filters) self.assertEqual(len(ctnrs), 1) def test_copy_to_container(self): @@ -212,31 +157,33 @@ class TestContainers(unittest.TestCase): vol: Optional[Volume] = None try: test_file_content = b"Hello World!" - vol = self.client.volumes.create("test-volume") - ctr = self.client.containers.create(image="alpine", - detach=True, - command="top", - volumes=["test-volume:/test-volume-read-only:ro"]) + vol = self.docker.volumes.create("test-volume") + ctr = self.docker.containers.create( + image="alpine", + detach=True, + command="top", + volumes=["test-volume:/test-volume-read-only:ro"], + ) ctr.start() buff: IO[bytes] = io.BytesIO() - with tarfile.open(fileobj=buff, mode="w:") as tf: - ti: tarfile.TarInfo = tarfile.TarInfo() - ti.uid = 1042 - ti.gid = 1043 - ti.name = "a.txt" - ti.path = "a.txt" - ti.mode = 0o644 - ti.type = tarfile.REGTYPE - ti.size = len(test_file_content) - tf.addfile(ti, fileobj=io.BytesIO(test_file_content)) + with tarfile.open(fileobj=buff, mode="w:") as file: + info: tarfile.TarInfo = tarfile.TarInfo() + info.uid = 1042 + info.gid = 1043 + info.name = "a.txt" + info.path = "a.txt" + info.mode = 0o644 + info.type = tarfile.REGTYPE + info.size = len(test_file_content) + file.addfile(info, fileobj=io.BytesIO(test_file_content)) buff.seek(0) ctr.put_archive("/tmp/", buff) ret, out = ctr.exec_run(["stat", "-c", "%u:%g", "/tmp/a.txt"]) self.assertEqual(ret, 0) - self.assertEqual(out.rstrip(), b'1042:1043', "UID/GID of copied file") + self.assertEqual(out.rstrip(), b"1042:1043", "UID/GID of copied file") ret, out = ctr.exec_run(["cat", "/tmp/a.txt"]) self.assertEqual(ret, 0) @@ -253,48 +200,61 @@ class TestContainers(unittest.TestCase): vol.remove(force=True) def test_mount_preexisting_dir(self): - dockerfile = (B'FROM quay.io/libpod/alpine:latest\n' - B'USER root\n' - B'RUN mkdir -p /workspace\n' - B'RUN chown 1042:1043 /workspace') + dockerfile = ( + b"FROM quay.io/libpod/alpine:latest\n" + b"USER root\n" + b"RUN mkdir -p /workspace\n" + b"RUN chown 1042:1043 /workspace" + ) img: Image - img, out = self.client.images.build(fileobj=io.BytesIO(dockerfile)) - ctr: Container = self.client.containers.create(image=img.id, detach=True, command="top", - volumes=["test_mount_preexisting_dir_vol:/workspace"]) + img, out = self.docker.images.build(fileobj=io.BytesIO(dockerfile)) + ctr: Container = self.docker.containers.create( + image=img.id, + detach=True, + command="top", + volumes=["test_mount_preexisting_dir_vol:/workspace"], + ) ctr.start() - ret, out = ctr.exec_run(["stat", "-c", "%u:%g", "/workspace"]) - self.assertEqual(out.rstrip(), b'1042:1043', "UID/GID set in dockerfile") - + _, out = ctr.exec_run(["stat", "-c", "%u:%g", "/workspace"]) + self.assertEqual(out.rstrip(), b"1042:1043", "UID/GID set in dockerfile") def test_non_existant_workdir(self): - dockerfile = (B'FROM quay.io/libpod/alpine:latest\n' - B'USER root\n' - B'WORKDIR /workspace/scratch\n' - B'RUN touch test') + dockerfile = ( + b"FROM quay.io/libpod/alpine:latest\n" + b"USER root\n" + b"WORKDIR /workspace/scratch\n" + b"RUN touch test" + ) img: Image - img, out = self.client.images.build(fileobj=io.BytesIO(dockerfile)) - ctr: Container = self.client.containers.create(image=img.id, detach=True, command="top", - volumes=["test_non_existant_workdir:/workspace"]) + img, _ = self.docker.images.build(fileobj=io.BytesIO(dockerfile)) + ctr: Container = self.docker.containers.create( + image=img.id, + detach=True, + command="top", + volumes=["test_non_existant_workdir:/workspace"], + ) ctr.start() - ret, out = ctr.exec_run(["stat", "/workspace/scratch/test"]) + ret, _ = ctr.exec_run(["stat", "/workspace/scratch/test"]) self.assertEqual(ret, 0, "Working directory created if it doesn't exist") def test_mount_rw_by_default(self): ctr: Optional[Container] = None vol: Optional[Volume] = None + try: - vol = self.client.volumes.create("test-volume") - ctr = self.client.containers.create(image="alpine", - detach=True, - command="top", - mounts=[Mount(target="/vol-mnt", - source="test-volume", - type='volume', - read_only=False)]) - ctr_inspect = self.client.api.inspect_container(ctr.id) + vol = self.docker.volumes.create("test-volume") + ctr = self.docker.containers.create( + image="alpine", + detach=True, + command="top", + mounts=[ + Mount(target="/vol-mnt", source="test-volume", type="volume", read_only=False) + ], + ) + ctr_inspect = self.docker.api.inspect_container(ctr.id) binds: List[str] = ctr_inspect["HostConfig"]["Binds"] self.assertEqual(len(binds), 1) - self.assertEqual(binds[0], 'test-volume:/vol-mnt:rw,rprivate,nosuid,nodev,rbind') + self.assertEqual(binds[0], "test-volume:/vol-mnt:rw,rprivate,nosuid,nodev,rbind") finally: if ctr is not None: ctr.remove() diff --git a/test/python/docker/compat/test_images.py b/test/python/docker/compat/test_images.py index 05d0e3e12..ac9a8d671 100644 --- a/test/python/docker/compat/test_images.py +++ b/test/python/docker/compat/test_images.py @@ -1,116 +1,73 @@ -import collections +""" +Integration tests for exercising docker-py against Podman Service. +""" import io import os -import subprocess -import sys -import time import unittest -from docker import DockerClient, errors -from docker.errors import APIError +from docker import errors -from test.python.docker import Podman +# pylint: disable=no-name-in-module,import-error,wrong-import-order from test.python.docker.compat import common, constant -class TestImages(unittest.TestCase): - podman = None # initialized podman configuration for tests - service = None # podman service instance - - def setUp(self): - super().setUp() - self.client = DockerClient(base_url="tcp://127.0.0.1:8080", timeout=15) - - TestImages.podman.restore_image_from_cache(self.client) - - def tearDown(self): - common.remove_all_images(self.client) - self.client.close() - return super().tearDown() - - @classmethod - def setUpClass(cls): - super().setUpClass() - TestImages.podman = Podman() - TestImages.service = TestImages.podman.open( - "system", "service", "tcp:127.0.0.1:8080", "--time=0" - ) - # give the service some time to be ready... - time.sleep(2) - - returncode = TestImages.service.poll() - if returncode is not None: - raise subprocess.CalledProcessError(returncode, "podman system service") - - @classmethod - def tearDownClass(cls): - TestImages.service.terminate() - stdout, stderr = TestImages.service.communicate(timeout=0.5) - if stdout: - sys.stdout.write("\nImages Service Stdout:\n" + stdout.decode("utf-8")) - if stderr: - sys.stderr.write("\nImAges Service Stderr:\n" + stderr.decode("utf-8")) - - TestImages.podman.tear_down() - return super().tearDownClass() +class TestImages(common.DockerTestCase): + """TestCase for exercising images.""" def test_tag_valid_image(self): """Validates if the image is tagged successfully""" - alpine = self.client.images.get(constant.ALPINE) + alpine = self.docker.images.get(constant.ALPINE) self.assertTrue(alpine.tag("demo", constant.ALPINE_SHORTNAME)) - alpine = self.client.images.get(constant.ALPINE) - for t in alpine.tags: - self.assertIn("alpine", t) + alpine = self.docker.images.get(constant.ALPINE) + for tag in alpine.tags: + self.assertIn("alpine", tag) - # @unittest.skip("doesn't work now") def test_retag_valid_image(self): - """Validates if name updates when the image is retagged""" - alpine = self.client.images.get(constant.ALPINE) + """Validates if name updates when the image is re-tagged.""" + alpine = self.docker.images.get(constant.ALPINE) self.assertTrue(alpine.tag("demo", "rename")) - alpine = self.client.images.get(constant.ALPINE) + alpine = self.docker.images.get(constant.ALPINE) self.assertNotIn("demo:test", alpine.tags) def test_list_images(self): """List images""" - self.assertEqual(len(self.client.images.list()), 1) + self.assertEqual(len(self.docker.images.list()), 1) # Add more images - self.client.images.pull(constant.BB) - self.assertEqual(len(self.client.images.list()), 2) - self.assertEqual(len(self.client.images.list(all=True)), 2) + self.docker.images.pull(constant.BB) + self.assertEqual(len(self.docker.images.list()), 2) + self.assertEqual(len(self.docker.images.list(all=True)), 2) # List images with filter - self.assertEqual(len(self.client.images.list(filters={"reference": "alpine"})), 1) + self.assertEqual(len(self.docker.images.list(filters={"reference": "alpine"})), 1) def test_search_image(self): """Search for image""" - for r in self.client.images.search("alpine"): + for registry in self.docker.images.search("alpine"): # registry matches if string is in either one - self.assertIn("alpine", r["Name"]+" "+r["Description"].lower()) + self.assertIn("alpine", registry["Name"] + " " + registry["Description"].lower()) def test_search_bogus_image(self): """Search for bogus image should throw exception""" - try: - r = self.client.images.search("bogus/bogus") - except: - return - self.assertTrue(len(r) == 0) + with self.assertRaises(errors.APIError): + self.docker.images.search("bogus/bogus") def test_remove_image(self): """Remove image""" # Check for error with wrong image name with self.assertRaises(errors.NotFound): - self.client.images.remove("dummy") - self.assertEqual(len(self.client.images.list()), 1) + self.docker.images.remove("dummy") - self.client.images.remove(constant.ALPINE) - self.assertEqual(len(self.client.images.list()), 0) + common.remove_all_containers(self.docker) + self.assertEqual(len(self.docker.images.list()), 1) + self.docker.images.remove(constant.ALPINE) + self.assertEqual(len(self.docker.images.list()), 0) def test_image_history(self): """Image history""" - img = self.client.images.get(constant.ALPINE) + img = self.docker.images.get(constant.ALPINE) history = img.history() image_id = img.id[7:] if img.id.startswith("sha256:") else img.id @@ -122,47 +79,49 @@ class TestImages(unittest.TestCase): def test_get_image_exists_not(self): """Negative test for get image""" with self.assertRaises(errors.NotFound): - response = self.client.images.get("image_does_not_exists") - collections.deque(response) + self.docker.images.get("image_does_not_exists") def test_save_image(self): """Export Image""" - image = self.client.images.pull(constant.BB) + image = self.docker.images.pull(constant.BB) file = os.path.join(TestImages.podman.image_cache, "busybox.tar") with open(file, mode="wb") as tarball: for frame in image.save(named=True): tarball.write(frame) - sz = os.path.getsize(file) - self.assertGreater(sz, 0) + self.assertGreater(os.path.getsize(file), 0) def test_load_image(self): """Import|Load Image""" - self.assertEqual(len(self.client.images.list()), 1) + self.assertEqual(len(self.docker.images.list()), 1) - image = self.client.images.pull(constant.BB) + image = self.docker.images.pull(constant.BB) file = os.path.join(TestImages.podman.image_cache, "busybox.tar") with open(file, mode="wb") as tarball: for frame in image.save(): tarball.write(frame) with open(file, mode="rb") as saved: - _ = self.client.images.load(saved) + self.docker.images.load(saved) - self.assertEqual(len(self.client.images.list()), 2) + self.assertEqual(len(self.docker.images.list()), 2) def test_load_corrupt_image(self): """Import|Load Image failure""" tarball = io.BytesIO("This is a corrupt tarball".encode("utf-8")) - with self.assertRaises(APIError): - self.client.images.load(tarball) + with self.assertRaises(errors.APIError): + self.docker.images.load(tarball) def test_build_image(self): + """Build Image with custom labels.""" labels = {"apple": "red", "grape": "green"} - _ = self.client.images.build( - path="test/python/docker/build_labels", labels=labels, tag="labels", isolation="default" + self.docker.images.build( + path="test/python/docker/build_labels", + labels=labels, + tag="labels", + isolation="default", ) - image = self.client.images.get("labels") + image = self.docker.images.get("labels") self.assertEqual(image.labels["apple"], labels["apple"]) self.assertEqual(image.labels["grape"], labels["grape"]) diff --git a/test/python/docker/compat/test_system.py b/test/python/docker/compat/test_system.py index a928de0ee..f6092996b 100644 --- a/test/python/docker/compat/test_system.py +++ b/test/python/docker/compat/test_system.py @@ -1,71 +1,32 @@ -import subprocess -import sys -import time -import unittest +""" +Integration tests for exercising docker-py against Podman Service. +""" -from docker import DockerClient - -from test.python.docker import Podman, constant +# pylint: disable=no-name-in-module,import-error,wrong-import-order +from test.python.docker import constant from test.python.docker.compat import common -class TestSystem(unittest.TestCase): - podman = None # initialized podman configuration for tests - service = None # podman service instance - topContainerId = "" - - def setUp(self): - super().setUp() - self.client = DockerClient(base_url="tcp://127.0.0.1:8080", timeout=15) - - TestSystem.podman.restore_image_from_cache(self.client) - TestSystem.topContainerId = common.run_top_container(self.client) - - def tearDown(self): - common.remove_all_containers(self.client) - common.remove_all_images(self.client) - self.client.close() - return super().tearDown() - - @classmethod - def setUpClass(cls): - super().setUpClass() - TestSystem.podman = Podman() - TestSystem.service = TestSystem.podman.open( - "system", "service", "tcp:127.0.0.1:8080", "--time=0" - ) - # give the service some time to be ready... - time.sleep(2) - - returncode = TestSystem.service.poll() - if returncode is not None: - raise subprocess.CalledProcessError(returncode, "podman system service") - - @classmethod - def tearDownClass(cls): - TestSystem.service.terminate() - stdout, stderr = TestSystem.service.communicate(timeout=0.5) - if stdout: - sys.stdout.write("\nImages Service Stdout:\n" + stdout.decode("utf-8")) - if stderr: - sys.stderr.write("\nImAges Service Stderr:\n" + stderr.decode("utf-8")) +# pylint: disable=missing-function-docstring +class TestSystem(common.DockerTestCase): + """TestCase for exercising Podman system services.""" - TestSystem.podman.tear_down() - return super().tearDownClass() - - def test_Info(self): - info = self.client.info() + def test_info(self): + info = self.docker.info() self.assertIsNotNone(info) self.assertEqual(info["RegistryConfig"]["IndexConfigs"]["localhost:5000"]["Secure"], False) - self.assertEqual(info["RegistryConfig"]["IndexConfigs"]["localhost:5000"]["Mirrors"], ["mirror.localhost:5000"]) + self.assertEqual( + info["RegistryConfig"]["IndexConfigs"]["localhost:5000"]["Mirrors"], + ["mirror.localhost:5000"], + ) def test_info_container_details(self): - info = self.client.info() + info = self.docker.info() self.assertEqual(info["Containers"], 1) - self.client.containers.create(image=constant.ALPINE) - info = self.client.info() + self.docker.containers.create(image=constant.ALPINE) + info = self.docker.info() self.assertEqual(info["Containers"], 2) def test_version(self): - version = self.client.version() + version = self.docker.version() self.assertIsNotNone(version["Platform"]["Name"]) diff --git a/test/python/docker/conftest.py b/test/python/docker/conftest.py new file mode 100644 index 000000000..c63780fa8 --- /dev/null +++ b/test/python/docker/conftest.py @@ -0,0 +1,8 @@ +""" +Configure pytest +""" + + +def pytest_report_header(config): + """Add header to report.""" + return "python client -- DockerClient" diff --git a/test/system/070-build.bats b/test/system/070-build.bats index c963d8325..e47d66542 100644 --- a/test/system/070-build.bats +++ b/test/system/070-build.bats @@ -210,6 +210,30 @@ EOF run_podman rmi -f build_test } +@test "podman parallel build should not race" { + skip_if_remote "following test is not supported for remote clients" + + # Run thirty parallel builds using the same Containerfile + cat >$PODMAN_TMPDIR/Containerfile <<EOF +FROM $IMAGE +RUN echo hi +EOF + + local count=30 + for i in $(seq --format '%02g' 1 $count); do + timeout --foreground -v --kill=10 60 \ + $PODMAN build -t i$i $PODMAN_TMPDIR &>/dev/null & + done + + # Wait for all background builds to complete. Note that this succeeds + # even if some of the individual builds fail! Our actual test is below. + wait + + # Now delete all built images. If any image wasn't built, rmi will fail + # and test will fail. + run_podman rmi $(seq --format 'i%02g' 1 $count) +} + @test "podman build - URLs" { tmpdir=$PODMAN_TMPDIR/build-test mkdir -p $tmpdir diff --git a/troubleshooting.md b/troubleshooting.md index 8bce8e50f..32f14c1ee 100644 --- a/troubleshooting.md +++ b/troubleshooting.md @@ -42,7 +42,7 @@ $ podman run -v ~/mycontent:/content:Z fedora touch /content/file Make sure the content is private for the container. Do not relabel system directories and content. Relabeling system content might cause other confined services on your machine to fail. For these -types of containers we recommend that disable SELinux separation. The option `--security-opt label=disable` +types of containers we recommend having SELinux separation disabled. The option `--security-opt label=disable` will disable SELinux separation for the container. $ podman run --security-opt label=disable -v ~:/home/user fedora touch /home/user/file @@ -157,7 +157,7 @@ When rootless Podman attempts to execute a container on a non exec home director #### Symptom If you are running Podman or Buildah on a home directory that is mounted noexec, -then they will fail. With a message like: +then they will fail with a message like: ``` podman run centos:7 @@ -166,7 +166,7 @@ standard_init_linux.go:203: exec user process caused "permission denied" #### Solution -Since the administrator of the system setup your home directory to be noexec, you will not be allowed to execute containers from storage in your home directory. It is possible to work around this by manually specifying a container storage path that is not on a noexec mount. Simply copy the file /etc/containers/storage.conf to ~/.config/containers/ (creating the directory if necessary). Specify a graphroot directory which is not on a noexec mount point and to which you have read/write privileges. You will need to modify other fields to writable directories as well. +Since the administrator of the system set up your home directory to be noexec, you will not be allowed to execute containers from storage in your home directory. It is possible to work around this by manually specifying a container storage path that is not on a noexec mount. Simply copy the file /etc/containers/storage.conf to ~/.config/containers/ (creating the directory if necessary). Specify a graphroot directory which is not on a noexec mount point and to which you have read/write privileges. You will need to modify other fields to writable directories as well. For example @@ -229,7 +229,7 @@ Rootless Podman requires the user running it to have a range of UIDs listed in / #### Symptom -An user, either via --user or through the default configured for the image, is not mapped inside the namespace. +A user, either via --user or through the default configured for the image, is not mapped inside the namespace. ``` podman run --rm -ti --user 1000000 alpine echo hi @@ -279,7 +279,7 @@ grep johndoe /etc/subuid /etc/subgid ### 11) Changing the location of the Graphroot leads to permission denied When I change the graphroot storage location in storage.conf, the next time I -run Podman I get an error like: +run Podman, I get an error like: ``` # podman run -p 5000:5000 -it centos bash @@ -323,7 +323,7 @@ Pulling an anonymous image that doesn't require authentication can result in an #### Symptom If you pull an anonymous image, one that should not require credentials, you can receive -and `invalid username/password` error if you have credentials established in the +an `invalid username/password` error if you have credentials established in the authentication file for the target container registry that are no longer valid. ``` @@ -991,12 +991,15 @@ less: dir1/a: Permission denied #### Solution -If you want to read or remove such a file, you can do so by entering a user namespace. -Instead of running commands such as `less dir1/a` or `rm dir1/a`, you would need to -prepend the command-line with `podman unshare`, i.e., -`podman unshare less dir1/a` or `podman unshare rm dir1/a`. To be able to use Bash -features, such as variable expansion and globbing, you need to wrap the command with -`bash -c`, e.g. `podman unshare bash -c 'ls $HOME/dir1/a*'`. +If you want to read, chown, or remove such a file, enter a user +namespace. Instead of running commands such as `less dir1/a` or `rm dir1/a`, you +need to prepend the command-line with `podman unshare`, i.e., +`podman unshare less dir1/a` or `podman unshare rm dir1/a`. To change the ownership +of the file _dir1/a_ to your regular user's UID and GID, run `podman unshare chown 0:0 dir1/a`. +A file having the ownership _0:0_ in the user namespace is owned by the regular +user on the host. To use Bash features, such as variable expansion and +globbing, you need to wrap the command with `bash -c`, e.g. +`podman unshare bash -c 'ls $HOME/dir1/a*'`. Would it have been possible to run Podman in another way so that your regular user would have become the owner of the file? Yes, you can use the options diff --git a/vendor/github.com/Microsoft/go-winio/backuptar/tar.go b/vendor/github.com/Microsoft/go-winio/backuptar/tar.go index 689e4da6b..2342a7fcd 100644 --- a/vendor/github.com/Microsoft/go-winio/backuptar/tar.go +++ b/vendor/github.com/Microsoft/go-winio/backuptar/tar.go @@ -113,6 +113,69 @@ func BasicInfoHeader(name string, size int64, fileInfo *winio.FileBasicInfo) *ta return hdr } +// SecurityDescriptorFromTarHeader reads the SDDL associated with the header of the current file +// from the tar header and returns the security descriptor into a byte slice. +func SecurityDescriptorFromTarHeader(hdr *tar.Header) ([]byte, error) { + // Maintaining old SDDL-based behavior for backward + // compatibility. All new tar headers written by this library + // will have raw binary for the security descriptor. + var sd []byte + var err error + if sddl, ok := hdr.PAXRecords[hdrSecurityDescriptor]; ok { + sd, err = winio.SddlToSecurityDescriptor(sddl) + if err != nil { + return nil, err + } + } + if sdraw, ok := hdr.PAXRecords[hdrRawSecurityDescriptor]; ok { + sd, err = base64.StdEncoding.DecodeString(sdraw) + if err != nil { + return nil, err + } + } + return sd, nil +} + +// ExtendedAttributesFromTarHeader reads the EAs associated with the header of the +// current file from the tar header and returns it as a byte slice. +func ExtendedAttributesFromTarHeader(hdr *tar.Header) ([]byte, error) { + var eas []winio.ExtendedAttribute + var eadata []byte + var err error + for k, v := range hdr.PAXRecords { + if !strings.HasPrefix(k, hdrEaPrefix) { + continue + } + data, err := base64.StdEncoding.DecodeString(v) + if err != nil { + return nil, err + } + eas = append(eas, winio.ExtendedAttribute{ + Name: k[len(hdrEaPrefix):], + Value: data, + }) + } + if len(eas) != 0 { + eadata, err = winio.EncodeExtendedAttributes(eas) + if err != nil { + return nil, err + } + } + return eadata, nil +} + +// EncodeReparsePointFromTarHeader reads the ReparsePoint structure from the tar header +// and encodes it into a byte slice. The file for which this function is called must be a +// symlink. +func EncodeReparsePointFromTarHeader(hdr *tar.Header) []byte { + _, isMountPoint := hdr.PAXRecords[hdrMountPoint] + rp := winio.ReparsePoint{ + Target: filepath.FromSlash(hdr.Linkname), + IsMountPoint: isMountPoint, + } + return winio.EncodeReparsePoint(&rp) +} + // WriteTarFileFromBackupStream writes a file to a tar writer using data from a Win32 backup stream. // // This encodes Win32 metadata as tar pax vendor extensions starting with MSWINDOWS. @@ -358,21 +421,10 @@ func FileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *win // tar file that was not processed, or io.EOF is there are no more. func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) (*tar.Header, error) { bw := winio.NewBackupStreamWriter(w) - var sd []byte - var err error - // Maintaining old SDDL-based behavior for backward compatibility. All new tar headers written - // by this library will have raw binary for the security descriptor. - if sddl, ok := hdr.PAXRecords[hdrSecurityDescriptor]; ok { - sd, err = winio.SddlToSecurityDescriptor(sddl) - if err != nil { - return nil, err - } - } - if sdraw, ok := hdr.PAXRecords[hdrRawSecurityDescriptor]; ok { - sd, err = base64.StdEncoding.DecodeString(sdraw) - if err != nil { - return nil, err - } + + sd, err := SecurityDescriptorFromTarHeader(hdr) + if err != nil { + return nil, err } if len(sd) != 0 { bhdr := winio.BackupHeader{ @@ -388,25 +440,12 @@ func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) ( return nil, err } } - var eas []winio.ExtendedAttribute - for k, v := range hdr.PAXRecords { - if !strings.HasPrefix(k, hdrEaPrefix) { - continue - } - data, err := base64.StdEncoding.DecodeString(v) - if err != nil { - return nil, err - } - eas = append(eas, winio.ExtendedAttribute{ - Name: k[len(hdrEaPrefix):], - Value: data, - }) + + eadata, err := ExtendedAttributesFromTarHeader(hdr) + if err != nil { + return nil, err } - if len(eas) != 0 { - eadata, err := winio.EncodeExtendedAttributes(eas) - if err != nil { - return nil, err - } + if len(eadata) != 0 { bhdr := winio.BackupHeader{ Id: winio.BackupEaData, Size: int64(len(eadata)), @@ -420,13 +459,9 @@ func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) ( return nil, err } } + if hdr.Typeflag == tar.TypeSymlink { - _, isMountPoint := hdr.PAXRecords[hdrMountPoint] - rp := winio.ReparsePoint{ - Target: filepath.FromSlash(hdr.Linkname), - IsMountPoint: isMountPoint, - } - reparse := winio.EncodeReparsePoint(&rp) + reparse := EncodeReparsePointFromTarHeader(hdr) bhdr := winio.BackupHeader{ Id: winio.BackupReparseData, Size: int64(len(reparse)), @@ -439,7 +474,9 @@ func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) ( if err != nil { return nil, err } + } + if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA { bhdr := winio.BackupHeader{ Id: winio.BackupData, diff --git a/vendor/github.com/Microsoft/go-winio/file.go b/vendor/github.com/Microsoft/go-winio/file.go index 0385e4108..293ab54c8 100644 --- a/vendor/github.com/Microsoft/go-winio/file.go +++ b/vendor/github.com/Microsoft/go-winio/file.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package winio @@ -143,6 +144,11 @@ func (f *win32File) Close() error { return nil } +// IsClosed checks if the file has been closed +func (f *win32File) IsClosed() bool { + return f.closing.isSet() +} + // prepareIo prepares for a new IO operation. // The caller must call f.wg.Done() when the IO is finished, prior to Close() returning. func (f *win32File) prepareIo() (*ioOperation, error) { diff --git a/vendor/github.com/Microsoft/go-winio/go.mod b/vendor/github.com/Microsoft/go-winio/go.mod index 98a8dea0e..f39a608da 100644 --- a/vendor/github.com/Microsoft/go-winio/go.mod +++ b/vendor/github.com/Microsoft/go-winio/go.mod @@ -1,9 +1,8 @@ module github.com/Microsoft/go-winio -go 1.12 +go 1.13 require ( - github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.7.0 golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c ) diff --git a/vendor/github.com/Microsoft/go-winio/go.sum b/vendor/github.com/Microsoft/go-winio/go.sum index aa6ad3b57..9bdcd9cfd 100644 --- a/vendor/github.com/Microsoft/go-winio/go.sum +++ b/vendor/github.com/Microsoft/go-winio/go.sum @@ -1,14 +1,11 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/Microsoft/go-winio/hvsock.go b/vendor/github.com/Microsoft/go-winio/hvsock.go index b632f8f8b..b2b644d00 100644 --- a/vendor/github.com/Microsoft/go-winio/hvsock.go +++ b/vendor/github.com/Microsoft/go-winio/hvsock.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package winio @@ -252,15 +253,23 @@ func (conn *HvsockConn) Close() error { return conn.sock.Close() } +func (conn *HvsockConn) IsClosed() bool { + return conn.sock.IsClosed() +} + func (conn *HvsockConn) shutdown(how int) error { - err := syscall.Shutdown(conn.sock.handle, syscall.SHUT_RD) + if conn.IsClosed() { + return ErrFileClosed + } + + err := syscall.Shutdown(conn.sock.handle, how) if err != nil { return os.NewSyscallError("shutdown", err) } return nil } -// CloseRead shuts down the read end of the socket. +// CloseRead shuts down the read end of the socket, preventing future read operations. func (conn *HvsockConn) CloseRead() error { err := conn.shutdown(syscall.SHUT_RD) if err != nil { @@ -269,8 +278,8 @@ func (conn *HvsockConn) CloseRead() error { return nil } -// CloseWrite shuts down the write end of the socket, notifying the other endpoint that -// no more data will be written. +// CloseWrite shuts down the write end of the socket, preventing future write operations and +// notifying the other endpoint that no more data will be written. func (conn *HvsockConn) CloseWrite() error { err := conn.shutdown(syscall.SHUT_WR) if err != nil { diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go index f497c0e39..2d9161e2d 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go @@ -14,8 +14,6 @@ import ( "encoding/binary" "fmt" "strconv" - - "golang.org/x/sys/windows" ) // Variant specifies which GUID variant (or "type") of the GUID. It determines @@ -41,13 +39,6 @@ type Version uint8 var _ = (encoding.TextMarshaler)(GUID{}) var _ = (encoding.TextUnmarshaler)(&GUID{}) -// GUID represents a GUID/UUID. It has the same structure as -// golang.org/x/sys/windows.GUID so that it can be used with functions expecting -// that type. It is defined as its own type so that stringification and -// marshaling can be supported. The representation matches that used by native -// Windows code. -type GUID windows.GUID - // NewV4 returns a new version 4 (pseudorandom) GUID, as defined by RFC 4122. func NewV4() (GUID, error) { var b [16]byte diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go new file mode 100644 index 000000000..f64d828c0 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go @@ -0,0 +1,15 @@ +// +build !windows + +package guid + +// GUID represents a GUID/UUID. It has the same structure as +// golang.org/x/sys/windows.GUID so that it can be used with functions expecting +// that type. It is defined as its own type as that is only available to builds +// targeted at `windows`. The representation matches that used by native Windows +// code. +type GUID struct { + Data1 uint32 + Data2 uint16 + Data3 uint16 + Data4 [8]byte +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go new file mode 100644 index 000000000..83617f4ee --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go @@ -0,0 +1,10 @@ +package guid + +import "golang.org/x/sys/windows" + +// GUID represents a GUID/UUID. It has the same structure as +// golang.org/x/sys/windows.GUID so that it can be used with functions expecting +// that type. It is defined as its own type so that stringification and +// marshaling can be supported. The representation matches that used by native +// Windows code. +type GUID windows.GUID diff --git a/vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go b/vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go index fca241590..602920786 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go @@ -3,11 +3,10 @@ package security import ( + "fmt" "os" "syscall" "unsafe" - - "github.com/pkg/errors" ) type ( @@ -72,7 +71,7 @@ func GrantVmGroupAccess(name string) error { // Stat (to determine if `name` is a directory). s, err := os.Stat(name) if err != nil { - return errors.Wrapf(err, "%s os.Stat %s", gvmga, name) + return fmt.Errorf("%s os.Stat %s: %w", gvmga, name, err) } // Get a handle to the file/directory. Must defer Close on success. @@ -88,7 +87,7 @@ func GrantVmGroupAccess(name string) error { sd := uintptr(0) origDACL := uintptr(0) if err := getSecurityInfo(fd, uint32(ot), uint32(si), nil, nil, &origDACL, nil, &sd); err != nil { - return errors.Wrapf(err, "%s GetSecurityInfo %s", gvmga, name) + return fmt.Errorf("%s GetSecurityInfo %s: %w", gvmga, name, err) } defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(sd))) @@ -102,7 +101,7 @@ func GrantVmGroupAccess(name string) error { // And finally use SetSecurityInfo to apply the updated DACL. if err := setSecurityInfo(fd, uint32(ot), uint32(si), uintptr(0), uintptr(0), newDACL, uintptr(0)); err != nil { - return errors.Wrapf(err, "%s SetSecurityInfo %s", gvmga, name) + return fmt.Errorf("%s SetSecurityInfo %s: %w", gvmga, name, err) } return nil @@ -120,7 +119,7 @@ func createFile(name string, isDir bool) (syscall.Handle, error) { } fd, err := syscall.CreateFile(&namep[0], da, sm, nil, syscall.OPEN_EXISTING, fa, 0) if err != nil { - return 0, errors.Wrapf(err, "%s syscall.CreateFile %s", gvmga, name) + return 0, fmt.Errorf("%s syscall.CreateFile %s: %w", gvmga, name, err) } return fd, nil } @@ -131,7 +130,7 @@ func generateDACLWithAcesAdded(name string, isDir bool, origDACL uintptr) (uintp // Generate pointers to the SIDs based on the string SIDs sid, err := syscall.StringToSid(sidVmGroup) if err != nil { - return 0, errors.Wrapf(err, "%s syscall.StringToSid %s %s", gvmga, name, sidVmGroup) + return 0, fmt.Errorf("%s syscall.StringToSid %s %s: %w", gvmga, name, sidVmGroup, err) } inheritance := inheritModeNoInheritance @@ -154,7 +153,7 @@ func generateDACLWithAcesAdded(name string, isDir bool, origDACL uintptr) (uintp modifiedDACL := uintptr(0) if err := setEntriesInAcl(uintptr(uint32(1)), uintptr(unsafe.Pointer(&eaArray[0])), origDACL, &modifiedDACL); err != nil { - return 0, errors.Wrapf(err, "%s SetEntriesInAcl %s", gvmga, name) + return 0, fmt.Errorf("%s SetEntriesInAcl %s: %w", gvmga, name, err) } return modifiedDACL, nil diff --git a/vendor/github.com/Microsoft/go-winio/vhd/vhd.go b/vendor/github.com/Microsoft/go-winio/vhd/vhd.go index a33a36c0f..f7f78fc23 100644 --- a/vendor/github.com/Microsoft/go-winio/vhd/vhd.go +++ b/vendor/github.com/Microsoft/go-winio/vhd/vhd.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package vhd @@ -7,14 +8,13 @@ import ( "syscall" "github.com/Microsoft/go-winio/pkg/guid" - "github.com/pkg/errors" "golang.org/x/sys/windows" ) //go:generate go run mksyscall_windows.go -output zvhd_windows.go vhd.go //sys createVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (win32err error) = virtdisk.CreateVirtualDisk -//sys openVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *OpenVirtualDiskParameters, handle *syscall.Handle) (win32err error) = virtdisk.OpenVirtualDisk +//sys openVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (win32err error) = virtdisk.OpenVirtualDisk //sys attachVirtualDisk(handle syscall.Handle, securityDescriptor *uintptr, attachVirtualDiskFlag uint32, providerSpecificFlags uint32, parameters *AttachVirtualDiskParameters, overlapped *syscall.Overlapped) (win32err error) = virtdisk.AttachVirtualDisk //sys detachVirtualDisk(handle syscall.Handle, detachVirtualDiskFlags uint32, providerSpecificFlags uint32) (win32err error) = virtdisk.DetachVirtualDisk //sys getVirtualDiskPhysicalPath(handle syscall.Handle, diskPathSizeInBytes *uint32, buffer *uint16) (win32err error) = virtdisk.GetVirtualDiskPhysicalPath @@ -62,13 +62,27 @@ type OpenVirtualDiskParameters struct { Version2 OpenVersion2 } +// The higher level `OpenVersion2` struct uses bools to refer to `GetInfoOnly` and `ReadOnly` for ease of use. However, +// the internal windows structure uses `BOOLS` aka int32s for these types. `openVersion2` is used for translating +// `OpenVersion2` fields to the correct windows internal field types on the `Open____` methods. +type openVersion2 struct { + getInfoOnly int32 + readOnly int32 + resiliencyGUID guid.GUID +} + +type openVirtualDiskParameters struct { + version uint32 + version2 openVersion2 +} + type AttachVersion2 struct { RestrictedOffset uint64 RestrictedLength uint64 } type AttachVirtualDiskParameters struct { - Version uint32 // Must always be set to 2 + Version uint32 Version2 AttachVersion2 } @@ -146,16 +160,13 @@ func CreateVhdx(path string, maxSizeInGb, blockSizeInMb uint32) error { return err } - if err := syscall.CloseHandle(handle); err != nil { - return err - } - return nil + return syscall.CloseHandle(handle) } // DetachVirtualDisk detaches a virtual hard disk by handle. func DetachVirtualDisk(handle syscall.Handle) (err error) { if err := detachVirtualDisk(handle, 0, 0); err != nil { - return errors.Wrap(err, "failed to detach virtual disk") + return fmt.Errorf("failed to detach virtual disk: %w", err) } return nil } @@ -185,7 +196,7 @@ func AttachVirtualDisk(handle syscall.Handle, attachVirtualDiskFlag AttachVirtua parameters, nil, ); err != nil { - return errors.Wrap(err, "failed to attach virtual disk") + return fmt.Errorf("failed to attach virtual disk: %w", err) } return nil } @@ -209,7 +220,7 @@ func AttachVhd(path string) (err error) { AttachVirtualDiskFlagNone, ¶ms, ); err != nil { - return errors.Wrap(err, "failed to attach virtual disk") + return fmt.Errorf("failed to attach virtual disk: %w", err) } return nil } @@ -234,19 +245,35 @@ func OpenVirtualDiskWithParameters(vhdPath string, virtualDiskAccessMask Virtual var ( handle syscall.Handle defaultType VirtualStorageType + getInfoOnly int32 + readOnly int32 ) if parameters.Version != 2 { return handle, fmt.Errorf("only version 2 VHDs are supported, found version: %d", parameters.Version) } + if parameters.Version2.GetInfoOnly { + getInfoOnly = 1 + } + if parameters.Version2.ReadOnly { + readOnly = 1 + } + params := &openVirtualDiskParameters{ + version: parameters.Version, + version2: openVersion2{ + getInfoOnly, + readOnly, + parameters.Version2.ResiliencyGUID, + }, + } if err := openVirtualDisk( &defaultType, vhdPath, uint32(virtualDiskAccessMask), uint32(openVirtualDiskFlags), - parameters, + params, &handle, ); err != nil { - return 0, errors.Wrap(err, "failed to open virtual disk") + return 0, fmt.Errorf("failed to open virtual disk: %w", err) } return handle, nil } @@ -272,7 +299,7 @@ func CreateVirtualDisk(path string, virtualDiskAccessMask VirtualDiskAccessMask, nil, &handle, ); err != nil { - return handle, errors.Wrap(err, "failed to create virtual disk") + return handle, fmt.Errorf("failed to create virtual disk: %w", err) } return handle, nil } @@ -290,7 +317,7 @@ func GetVirtualDiskPhysicalPath(handle syscall.Handle) (_ string, err error) { &diskPathSizeInBytes, &diskPhysicalPathBuf[0], ); err != nil { - return "", errors.Wrap(err, "failed to get disk physical path") + return "", fmt.Errorf("failed to get disk physical path: %w", err) } return windows.UTF16ToString(diskPhysicalPathBuf[:]), nil } @@ -314,10 +341,10 @@ func CreateDiffVhd(diffVhdPath, baseVhdPath string, blockSizeInMB uint32) error createParams, ) if err != nil { - return fmt.Errorf("failed to create differencing vhd: %s", err) + return fmt.Errorf("failed to create differencing vhd: %w", err) } if err := syscall.CloseHandle(vhdHandle); err != nil { - return fmt.Errorf("failed to close differencing vhd handle: %s", err) + return fmt.Errorf("failed to close differencing vhd handle: %w", err) } return nil } diff --git a/vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go b/vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go index 7fb5f3651..1d7498db3 100644 --- a/vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go +++ b/vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go @@ -88,7 +88,7 @@ func getVirtualDiskPhysicalPath(handle syscall.Handle, diskPathSizeInBytes *uint return } -func openVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *OpenVirtualDiskParameters, handle *syscall.Handle) (win32err error) { +func openVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (win32err error) { var _p0 *uint16 _p0, win32err = syscall.UTF16PtrFromString(path) if win32err != nil { @@ -97,7 +97,7 @@ func openVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtua return _openVirtualDisk(virtualStorageType, _p0, virtualDiskAccessMask, openVirtualDiskFlags, parameters, handle) } -func _openVirtualDisk(virtualStorageType *VirtualStorageType, path *uint16, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *OpenVirtualDiskParameters, handle *syscall.Handle) (win32err error) { +func _openVirtualDisk(virtualStorageType *VirtualStorageType, path *uint16, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (win32err error) { r0, _, _ := syscall.Syscall6(procOpenVirtualDisk.Addr(), 6, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(openVirtualDiskFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(handle))) if r0 != 0 { win32err = syscall.Errno(r0) diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/build.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/build.go index 708b26689..9ee97fc91 100644 --- a/vendor/github.com/containerd/stargz-snapshotter/estargz/build.go +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/build.go @@ -26,6 +26,7 @@ import ( "archive/tar" "bytes" "compress/gzip" + "errors" "fmt" "io" "io/ioutil" @@ -38,7 +39,6 @@ import ( "github.com/containerd/stargz-snapshotter/estargz/errorutil" "github.com/klauspost/compress/zstd" digest "github.com/opencontainers/go-digest" - "github.com/pkg/errors" "golang.org/x/sync/errgroup" ) @@ -142,7 +142,7 @@ func Build(tarBlob *io.SectionReader, opt ...Option) (_ *Blob, rErr error) { defer func() { if rErr != nil { if err := layerFiles.CleanupAll(); err != nil { - rErr = errors.Wrapf(rErr, "failed to cleanup tmp files: %v", err) + rErr = fmt.Errorf("failed to cleanup tmp files: %v: %w", err, rErr) } } }() @@ -307,7 +307,7 @@ func sortEntries(in io.ReaderAt, prioritized []string, missedPrioritized *[]stri // Import tar file. intar, err := importTar(in) if err != nil { - return nil, errors.Wrap(err, "failed to sort") + return nil, fmt.Errorf("failed to sort: %w", err) } // Sort the tar file respecting to the prioritized files list. @@ -318,7 +318,7 @@ func sortEntries(in io.ReaderAt, prioritized []string, missedPrioritized *[]stri *missedPrioritized = append(*missedPrioritized, l) continue // allow not found } - return nil, errors.Wrap(err, "failed to sort tar entries") + return nil, fmt.Errorf("failed to sort tar entries: %w", err) } } if len(prioritized) == 0 { @@ -371,7 +371,7 @@ func importTar(in io.ReaderAt) (*tarFile, error) { tf := &tarFile{} pw, err := newCountReader(in) if err != nil { - return nil, errors.Wrap(err, "failed to make position watcher") + return nil, fmt.Errorf("failed to make position watcher: %w", err) } tr := tar.NewReader(pw) @@ -383,7 +383,7 @@ func importTar(in io.ReaderAt) (*tarFile, error) { if err == io.EOF { break } else { - return nil, errors.Wrap(err, "failed to parse tar file") + return nil, fmt.Errorf("failed to parse tar file, %w", err) } } switch cleanEntryName(h.Name) { @@ -420,7 +420,7 @@ func moveRec(name string, in *tarFile, out *tarFile) error { _, okIn := in.get(name) _, okOut := out.get(name) if !okIn && !okOut { - return errors.Wrapf(errNotFound, "file: %q", name) + return fmt.Errorf("file: %q: %w", name, errNotFound) } parent, _ := path.Split(strings.TrimSuffix(name, "/")) diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go index 2c36e89b4..4b655c145 100644 --- a/vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go @@ -27,6 +27,7 @@ import ( "bytes" "compress/gzip" "crypto/sha256" + "errors" "fmt" "hash" "io" @@ -40,7 +41,6 @@ import ( "github.com/containerd/stargz-snapshotter/estargz/errorutil" digest "github.com/opencontainers/go-digest" - "github.com/pkg/errors" "github.com/vbatts/tar-split/archive/tar" ) @@ -385,8 +385,7 @@ func (r *Reader) Verifiers() (TOCEntryVerifier, error) { if e.Digest != "" { d, err := digest.Parse(e.Digest) if err != nil { - return nil, errors.Wrapf(err, - "failed to parse regular file digest %q", e.Digest) + return nil, fmt.Errorf("failed to parse regular file digest %q: %w", e.Digest, err) } regDigestMap[e.Offset] = d } else { @@ -401,8 +400,7 @@ func (r *Reader) Verifiers() (TOCEntryVerifier, error) { if e.ChunkDigest != "" { d, err := digest.Parse(e.ChunkDigest) if err != nil { - return nil, errors.Wrapf(err, - "failed to parse chunk digest %q", e.ChunkDigest) + return nil, fmt.Errorf("failed to parse chunk digest %q: %w", e.ChunkDigest, err) } chunkDigestMap[e.Offset] = d } else { @@ -647,7 +645,7 @@ func Unpack(sr *io.SectionReader, c Decompressor) (io.ReadCloser, error) { } blobPayloadSize, _, _, err := c.ParseFooter(footer) if err != nil { - return nil, errors.Wrapf(err, "failed to parse footer") + return nil, fmt.Errorf("failed to parse footer: %w", err) } return c.Reader(io.LimitReader(sr, blobPayloadSize)) } diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/go.mod b/vendor/github.com/containerd/stargz-snapshotter/estargz/go.mod index b82879fd7..9256b36b9 100644 --- a/vendor/github.com/containerd/stargz-snapshotter/estargz/go.mod +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/go.mod @@ -3,9 +3,8 @@ module github.com/containerd/stargz-snapshotter/estargz go 1.16 require ( - github.com/klauspost/compress v1.14.2 + github.com/klauspost/compress v1.14.3 github.com/opencontainers/go-digest v1.0.0 - github.com/pkg/errors v0.9.1 github.com/vbatts/tar-split v0.11.2 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a ) diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/go.sum b/vendor/github.com/containerd/stargz-snapshotter/estargz/go.sum index 20433e16b..800028d92 100644 --- a/vendor/github.com/containerd/stargz-snapshotter/estargz/go.sum +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/go.sum @@ -1,12 +1,10 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/klauspost/compress v1.14.2 h1:S0OHlFk/Gbon/yauFJ4FfJJF5V0fc5HbBTJazi28pRw= -github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.14.3 h1:DQv1WP+iS4srNjibdnHtqu8JNWCDMluj5NzPnFJsnvk= +github.com/klauspost/compress v1.14.3/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go index 7330849cb..591d7a62e 100644 --- a/vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go @@ -34,7 +34,6 @@ import ( "strconv" digest "github.com/opencontainers/go-digest" - "github.com/pkg/errors" ) type gzipCompression struct { @@ -150,7 +149,7 @@ func (gz *GzipDecompressor) ParseFooter(p []byte) (blobPayloadSize, tocOffset, t } tocOffset, err = strconv.ParseInt(string(subfield[:16]), 16, 64) if err != nil { - return 0, 0, 0, errors.Wrapf(err, "legacy: failed to parse toc offset") + return 0, 0, 0, fmt.Errorf("legacy: failed to parse toc offset: %w", err) } return tocOffset, tocOffset, 0, nil } @@ -179,7 +178,7 @@ func (gz *LegacyGzipDecompressor) ParseFooter(p []byte) (blobPayloadSize, tocOff } zr, err := gzip.NewReader(bytes.NewReader(p)) if err != nil { - return 0, 0, 0, errors.Wrapf(err, "legacy: failed to get footer gzip reader") + return 0, 0, 0, fmt.Errorf("legacy: failed to get footer gzip reader: %w", err) } defer zr.Close() extra := zr.Header.Extra @@ -191,7 +190,7 @@ func (gz *LegacyGzipDecompressor) ParseFooter(p []byte) (blobPayloadSize, tocOff } tocOffset, err = strconv.ParseInt(string(extra[:16]), 16, 64) if err != nil { - return 0, 0, 0, errors.Wrapf(err, "legacy: failed to parse toc offset") + return 0, 0, 0, fmt.Errorf("legacy: failed to parse toc offset: %w", err) } return tocOffset, tocOffset, 0, nil } diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go index 9224e456d..1de13a470 100644 --- a/vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go @@ -28,6 +28,7 @@ import ( "compress/gzip" "crypto/sha256" "encoding/json" + "errors" "fmt" "io" "io/ioutil" @@ -41,7 +42,6 @@ import ( "github.com/containerd/stargz-snapshotter/estargz/errorutil" "github.com/klauspost/compress/zstd" digest "github.com/opencontainers/go-digest" - "github.com/pkg/errors" ) // TestingController is Compression with some helper methods necessary for testing. @@ -1062,18 +1062,18 @@ func parseStargz(sgz *io.SectionReader, controller TestingController) (decodedJT fSize := controller.FooterSize() footer := make([]byte, fSize) if _, err := sgz.ReadAt(footer, sgz.Size()-fSize); err != nil { - return nil, 0, errors.Wrap(err, "error reading footer") + return nil, 0, fmt.Errorf("error reading footer: %w", err) } _, tocOffset, _, err := controller.ParseFooter(footer[positive(int64(len(footer))-fSize):]) if err != nil { - return nil, 0, errors.Wrapf(err, "failed to parse footer") + return nil, 0, fmt.Errorf("failed to parse footer: %w", err) } // Decode the TOC JSON tocReader := io.NewSectionReader(sgz, tocOffset, sgz.Size()-tocOffset-fSize) decodedJTOC, _, err = controller.ParseTOC(tocReader) if err != nil { - return nil, 0, errors.Wrap(err, "failed to parse TOC") + return nil, 0, fmt.Errorf("failed to parse TOC: %w", err) } return decodedJTOC, tocOffset, nil } diff --git a/vendor/github.com/containers/image/v5/oci/layout/oci_dest.go b/vendor/github.com/containers/image/v5/oci/layout/oci_dest.go index d0ee72635..c8156cc3a 100644 --- a/vendor/github.com/containers/image/v5/oci/layout/oci_dest.go +++ b/vendor/github.com/containers/image/v5/oci/layout/oci_dest.go @@ -112,7 +112,7 @@ func (d *ociImageDestination) IgnoresEmbeddedDockerReference() bool { // HasThreadSafePutBlob indicates whether PutBlob can be executed concurrently. func (d *ociImageDestination) HasThreadSafePutBlob() bool { - return false + return true } // PutBlob writes contents of stream and returns data representing the result. diff --git a/vendor/github.com/containers/image/v5/storage/storage_image.go b/vendor/github.com/containers/image/v5/storage/storage_image.go index bcb09c83c..08ae042ac 100644 --- a/vendor/github.com/containers/image/v5/storage/storage_image.go +++ b/vendor/github.com/containers/image/v5/storage/storage_image.go @@ -1197,21 +1197,13 @@ func (s *storageImageDestination) Commit(ctx context.Context, unparsedToplevel t } logrus.Debugf("saved image metadata %q", string(metadata)) } - // Set the reference's name on the image. We don't need to worry about avoiding duplicate - // values because SetNames() will deduplicate the list that we pass to it. - if name := s.imageRef.DockerReference(); len(oldNames) > 0 || name != nil { - names := []string{} - if name != nil { - names = append(names, name.String()) + // Adds the reference's name on the image. We don't need to worry about avoiding duplicate + // values because AddNames() will deduplicate the list that we pass to it. + if name := s.imageRef.DockerReference(); name != nil { + if err := s.imageRef.transport.store.AddNames(img.ID, []string{name.String()}); err != nil { + return errors.Wrapf(err, "adding names %v to image %q", name, img.ID) } - if len(oldNames) > 0 { - names = append(names, oldNames...) - } - if err := s.imageRef.transport.store.SetNames(img.ID, names); err != nil { - logrus.Debugf("error setting names %v on image %q: %v", names, img.ID, err) - return errors.Wrapf(err, "setting names %v on image %q", names, img.ID) - } - logrus.Debugf("set names of image %q to %v", img.ID, names) + logrus.Debugf("added name %q to image %q", name, img.ID) } commitSucceeded = true diff --git a/vendor/github.com/containers/image/v5/version/version.go b/vendor/github.com/containers/image/v5/version/version.go index a9163e059..05bb40fb4 100644 --- a/vendor/github.com/containers/image/v5/version/version.go +++ b/vendor/github.com/containers/image/v5/version/version.go @@ -6,9 +6,9 @@ const ( // VersionMajor is for an API incompatible changes VersionMajor = 5 // VersionMinor is for functionality in a backwards-compatible manner - VersionMinor = 19 + VersionMinor = 20 // VersionPatch is for backwards-compatible bug fixes - VersionPatch = 2 + VersionPatch = 1 // VersionDev indicates development branch. Releases will be empty string. VersionDev = "-dev" diff --git a/vendor/github.com/containers/storage/containers.go b/vendor/github.com/containers/storage/containers.go index 5425f0339..a8b20f03a 100644 --- a/vendor/github.com/containers/storage/containers.go +++ b/vendor/github.com/containers/storage/containers.go @@ -84,8 +84,17 @@ type ContainerStore interface { // SetNames updates the list of names associated with the container // with the specified ID. + // Deprecated: Prone to race conditions, suggested alternatives are `AddNames` and `RemoveNames`. SetNames(id string, names []string) error + // AddNames adds the supplied values to the list of names associated with the container with + // the specified id. + AddNames(id string, names []string) error + + // RemoveNames removes the supplied values from the list of names associated with the container with + // the specified id. + RemoveNames(id string, names []string) error + // Get retrieves information about a container given an ID or name. Get(id string) (*Container, error) @@ -377,22 +386,40 @@ func (r *containerStore) removeName(container *Container, name string) { container.Names = stringSliceWithoutValue(container.Names, name) } +// Deprecated: Prone to race conditions, suggested alternatives are `AddNames` and `RemoveNames`. func (r *containerStore) SetNames(id string, names []string) error { - names = dedupeNames(names) - if container, ok := r.lookup(id); ok { - for _, name := range container.Names { - delete(r.byname, name) - } - for _, name := range names { - if otherContainer, ok := r.byname[name]; ok { - r.removeName(otherContainer, name) - } - r.byname[name] = container + return r.updateNames(id, names, setNames) +} + +func (r *containerStore) AddNames(id string, names []string) error { + return r.updateNames(id, names, addNames) +} + +func (r *containerStore) RemoveNames(id string, names []string) error { + return r.updateNames(id, names, removeNames) +} + +func (r *containerStore) updateNames(id string, names []string, op updateNameOperation) error { + container, ok := r.lookup(id) + if !ok { + return ErrContainerUnknown + } + oldNames := container.Names + names, err := applyNameOperation(oldNames, names, op) + if err != nil { + return err + } + for _, name := range oldNames { + delete(r.byname, name) + } + for _, name := range names { + if otherContainer, ok := r.byname[name]; ok { + r.removeName(otherContainer, name) } - container.Names = names - return r.Save() + r.byname[name] = container } - return ErrContainerUnknown + container.Names = names + return r.Save() } func (r *containerStore) Delete(id string) error { diff --git a/vendor/github.com/containers/storage/drivers/chown.go b/vendor/github.com/containers/storage/drivers/chown.go index 63bfd2d13..2db6764c9 100644 --- a/vendor/github.com/containers/storage/drivers/chown.go +++ b/vendor/github.com/containers/storage/drivers/chown.go @@ -50,11 +50,14 @@ func chownByMapsMain() { if len(toHost.UIDs()) == 0 && len(toHost.GIDs()) == 0 { toHost = nil } + + chowner := newLChowner() + chown := func(path string, info os.FileInfo, _ error) error { if path == "." { return nil } - return platformLChown(path, info, toHost, toContainer) + return chowner.LChown(path, info, toHost, toContainer) } if err := pwalk.Walk(".", chown); err != nil { fmt.Fprintf(os.Stderr, "error during chown: %v", err) diff --git a/vendor/github.com/containers/storage/drivers/chown_unix.go b/vendor/github.com/containers/storage/drivers/chown_unix.go index 0387adfc1..76823d532 100644 --- a/vendor/github.com/containers/storage/drivers/chown_unix.go +++ b/vendor/github.com/containers/storage/drivers/chown_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package graphdriver @@ -6,17 +7,50 @@ import ( "errors" "fmt" "os" + "sync" "syscall" "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/system" ) -func platformLChown(path string, info os.FileInfo, toHost, toContainer *idtools.IDMappings) error { +type inode struct { + Dev uint64 + Ino uint64 +} + +type platformChowner struct { + mutex sync.Mutex + inodes map[inode]bool +} + +func newLChowner() *platformChowner { + return &platformChowner{ + inodes: make(map[inode]bool), + } +} + +func (c *platformChowner) LChown(path string, info os.FileInfo, toHost, toContainer *idtools.IDMappings) error { st, ok := info.Sys().(*syscall.Stat_t) if !ok { return nil } + + i := inode{ + Dev: uint64(st.Dev), + Ino: uint64(st.Ino), + } + c.mutex.Lock() + _, found := c.inodes[i] + if !found { + c.inodes[i] = true + } + c.mutex.Unlock() + + if found { + return nil + } + // Map an on-disk UID/GID pair from host to container // using the first map, then back to the host using the // second map. Skip that first step if they're 0, to diff --git a/vendor/github.com/containers/storage/drivers/chown_windows.go b/vendor/github.com/containers/storage/drivers/chown_windows.go index 31bd5bb52..1845a4e08 100644 --- a/vendor/github.com/containers/storage/drivers/chown_windows.go +++ b/vendor/github.com/containers/storage/drivers/chown_windows.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package graphdriver @@ -9,6 +10,13 @@ import ( "github.com/containers/storage/pkg/idtools" ) -func platformLChown(path string, info os.FileInfo, toHost, toContainer *idtools.IDMappings) error { +type platformChowner struct { +} + +func newLChowner() *platformChowner { + return &platformChowner{} +} + +func (c *platformChowner) LChown(path string, info os.FileInfo, toHost, toContainer *idtools.IDMappings) error { return &os.PathError{"lchown", path, syscall.EWINDOWS} } diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay.go b/vendor/github.com/containers/storage/drivers/overlay/overlay.go index e5355590b..739828b35 100644 --- a/vendor/github.com/containers/storage/drivers/overlay/overlay.go +++ b/vendor/github.com/containers/storage/drivers/overlay/overlay.go @@ -920,7 +920,9 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, disable defer func() { // Clean up on failure if retErr != nil { - os.RemoveAll(dir) + if err2 := os.RemoveAll(dir); err2 != nil { + logrus.Errorf("While recovering from a failure creating a layer, error deleting %#v: %v", dir, err2) + } } }() @@ -1253,6 +1255,8 @@ func (d *Driver) recreateSymlinks() error { linkFile := filepath.Join(d.dir(targetID), "link") data, err := ioutil.ReadFile(linkFile) if err != nil || string(data) != link.Name() { + // NOTE: If two or more links point to the same target, we will update linkFile + // with every value of link.Name(), and set madeProgress = true every time. if err := ioutil.WriteFile(linkFile, []byte(link.Name()), 0644); err != nil { errs = multierror.Append(errs, errors.Wrapf(err, "correcting link for layer %s", targetID)) continue @@ -1458,41 +1462,37 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO workdir := path.Join(dir, "work") - var opts string - if readWrite { - opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(absLowers, ":"), diffDir, workdir) - } else { - opts = fmt.Sprintf("lowerdir=%s:%s", diffDir, strings.Join(absLowers, ":")) - } - if len(optsList) > 0 { - opts = fmt.Sprintf("%s,%s", strings.Join(optsList, ","), opts) - } - if d.options.mountProgram == "" && unshare.IsRootless() { - opts = fmt.Sprintf("%s,userxattr", opts) + optsList = append(optsList, "userxattr") } - // If "volatile" is not supported by the file system, just ignore the request - if options.Volatile && !hasVolatileOption(strings.Split(opts, ",")) { + if options.Volatile && !hasVolatileOption(optsList) { supported, err := d.getSupportsVolatile() if err != nil { return "", err } + // If "volatile" is not supported by the file system, just ignore the request if supported { - opts = fmt.Sprintf("%s,volatile", opts) + optsList = append(optsList, "volatile") } } + var opts string + if readWrite { + opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(absLowers, ":"), diffDir, workdir) + } else { + opts = fmt.Sprintf("lowerdir=%s:%s", diffDir, strings.Join(absLowers, ":")) + } + if len(optsList) > 0 { + opts = fmt.Sprintf("%s,%s", opts, strings.Join(optsList, ",")) + } + mountData := label.FormatMountLabel(opts, options.MountLabel) mountFunc := unix.Mount mountTarget := mergedDir pageSize := unix.Getpagesize() - // Use relative paths and mountFrom when the mount data has exceeded - // the page size. The mount syscall fails if the mount data cannot - // fit within a page and relative links make the mount data much - // smaller at the expense of requiring a fork exec to chroot. if d.options.mountProgram != "" { mountFunc = func(source string, target string, mType string, flags uintptr, label string) error { if !disableShifting { @@ -1519,13 +1519,21 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO return nil } } else if len(mountData) > pageSize { + // Use relative paths and mountFrom when the mount data has exceeded + // the page size. The mount syscall fails if the mount data cannot + // fit within a page and relative links make the mount data much + // smaller at the expense of requiring a fork exec to chroot. + workdir = path.Join(id, "work") //FIXME: We need to figure out to get this to work with additional stores if readWrite { diffDir := path.Join(id, "diff") opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(relLowers, ":"), diffDir, workdir) } else { - opts = fmt.Sprintf("lowerdir=%s", strings.Join(absLowers, ":")) + opts = fmt.Sprintf("lowerdir=%s", strings.Join(relLowers, ":")) + } + if len(optsList) > 0 { + opts = fmt.Sprintf("%s,%s", opts, strings.Join(optsList, ",")) } mountData = label.FormatMountLabel(opts, options.MountLabel) if len(mountData) > pageSize { diff --git a/vendor/github.com/containers/storage/errors.go b/vendor/github.com/containers/storage/errors.go index 0b55639e6..de6e37754 100644 --- a/vendor/github.com/containers/storage/errors.go +++ b/vendor/github.com/containers/storage/errors.go @@ -1,6 +1,8 @@ package storage import ( + "errors" + "github.com/containers/storage/types" ) @@ -57,4 +59,7 @@ var ( ErrNotSupported = types.ErrNotSupported // ErrInvalidMappings is returned when the specified mappings are invalid. ErrInvalidMappings = types.ErrInvalidMappings + // ErrInvalidNameOperation is returned when updateName is called with invalid operation. + // Internal error + errInvalidUpdateNameOperation = errors.New("invalid update name operation") ) diff --git a/vendor/github.com/containers/storage/go.mod b/vendor/github.com/containers/storage/go.mod index e4c593ff8..f6a5f4bfd 100644 --- a/vendor/github.com/containers/storage/go.mod +++ b/vendor/github.com/containers/storage/go.mod @@ -4,15 +4,15 @@ module github.com/containers/storage require ( github.com/BurntSushi/toml v1.0.0 - github.com/Microsoft/go-winio v0.5.1 + github.com/Microsoft/go-winio v0.5.2 github.com/Microsoft/hcsshim v0.9.2 - github.com/containerd/stargz-snapshotter/estargz v0.11.0 + github.com/containerd/stargz-snapshotter/estargz v0.11.1 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/go-units v0.4.0 github.com/google/go-intervals v0.0.2 github.com/hashicorp/go-multierror v1.1.1 github.com/json-iterator/go v1.1.12 - github.com/klauspost/compress v1.14.2 + github.com/klauspost/compress v1.14.4 github.com/klauspost/pgzip v1.2.5 github.com/mattn/go-shellwords v1.0.12 github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible diff --git a/vendor/github.com/containers/storage/go.sum b/vendor/github.com/containers/storage/go.sum index d3e5cb009..e5bf06316 100644 --- a/vendor/github.com/containers/storage/go.sum +++ b/vendor/github.com/containers/storage/go.sum @@ -47,8 +47,8 @@ github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugX github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= -github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= @@ -176,8 +176,8 @@ github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFY github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= -github.com/containerd/stargz-snapshotter/estargz v0.11.0 h1:t0IW5kOmY7AXDAWRUs2uVzDhijAUOAYVr/dyRhOQvBg= -github.com/containerd/stargz-snapshotter/estargz v0.11.0/go.mod h1:/KsZXsJRllMbTKFfG0miFQWViQKdI9+9aSXs+HN0+ac= +github.com/containerd/stargz-snapshotter/estargz v0.11.1 h1:mNQqxcAWmDrV6d6yUvzFhfY8puNzoQz9v4diW+Pmei4= +github.com/containerd/stargz-snapshotter/estargz v0.11.1/go.mod h1:6VoPcf4M1wvnogWxqc4TqBWWErCS+R+ucnPZId2VbpQ= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= @@ -424,8 +424,9 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.14.2 h1:S0OHlFk/Gbon/yauFJ4FfJJF5V0fc5HbBTJazi28pRw= -github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.14.3/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.14.4 h1:eijASRJcobkVtSt81Olfh7JX43osYLwy5krOJo6YEu4= +github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= diff --git a/vendor/github.com/containers/storage/images.go b/vendor/github.com/containers/storage/images.go index bca25a65b..a4c3ed22c 100644 --- a/vendor/github.com/containers/storage/images.go +++ b/vendor/github.com/containers/storage/images.go @@ -136,8 +136,19 @@ type ImageStore interface { // SetNames replaces the list of names associated with an image with the // supplied values. The values are expected to be valid normalized // named image references. + // Deprecated: Prone to race conditions, suggested alternatives are `AddNames` and `RemoveNames`. SetNames(id string, names []string) error + // AddNames adds the supplied values to the list of names associated with the image with + // the specified id. The values are expected to be valid normalized + // named image references. + AddNames(id string, names []string) error + + // RemoveNames removes the supplied values from the list of names associated with the image with + // the specified id. The values are expected to be valid normalized + // named image references. + RemoveNames(id string, names []string) error + // Delete removes the record of the image. Delete(id string) error @@ -425,37 +436,36 @@ func (r *imageStore) Create(id string, names []string, layer, metadata string, c if created.IsZero() { created = time.Now().UTC() } - if err == nil { - image = &Image{ - ID: id, - Digest: searchableDigest, - Digests: nil, - Names: names, - TopLayer: layer, - Metadata: metadata, - BigDataNames: []string{}, - BigDataSizes: make(map[string]int64), - BigDataDigests: make(map[string]digest.Digest), - Created: created, - Flags: make(map[string]interface{}), - } - err := image.recomputeDigests() - if err != nil { - return nil, errors.Wrapf(err, "error validating digests for new image") - } - r.images = append(r.images, image) - r.idindex.Add(id) - r.byid[id] = image - for _, name := range names { - r.byname[name] = image - } - for _, digest := range image.Digests { - list := r.bydigest[digest] - r.bydigest[digest] = append(list, image) - } - err = r.Save() - image = copyImage(image) + + image = &Image{ + ID: id, + Digest: searchableDigest, + Digests: nil, + Names: names, + TopLayer: layer, + Metadata: metadata, + BigDataNames: []string{}, + BigDataSizes: make(map[string]int64), + BigDataDigests: make(map[string]digest.Digest), + Created: created, + Flags: make(map[string]interface{}), + } + err = image.recomputeDigests() + if err != nil { + return nil, errors.Wrapf(err, "error validating digests for new image") + } + r.images = append(r.images, image) + r.idindex.Add(id) + r.byid[id] = image + for _, name := range names { + r.byname[name] = image + } + for _, digest := range image.Digests { + list := r.bydigest[digest] + r.bydigest[digest] = append(list, image) } + err = r.Save() + image = copyImage(image) return image, err } @@ -506,26 +516,44 @@ func (i *Image) addNameToHistory(name string) { i.NamesHistory = dedupeNames(append([]string{name}, i.NamesHistory...)) } +// Deprecated: Prone to race conditions, suggested alternatives are `AddNames` and `RemoveNames`. func (r *imageStore) SetNames(id string, names []string) error { + return r.updateNames(id, names, setNames) +} + +func (r *imageStore) AddNames(id string, names []string) error { + return r.updateNames(id, names, addNames) +} + +func (r *imageStore) RemoveNames(id string, names []string) error { + return r.updateNames(id, names, removeNames) +} + +func (r *imageStore) updateNames(id string, names []string, op updateNameOperation) error { if !r.IsReadWrite() { return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to change image name assignments at %q", r.imagespath()) } - names = dedupeNames(names) - if image, ok := r.lookup(id); ok { - for _, name := range image.Names { - delete(r.byname, name) - } - for _, name := range names { - if otherImage, ok := r.byname[name]; ok { - r.removeName(otherImage, name) - } - r.byname[name] = image - image.addNameToHistory(name) + image, ok := r.lookup(id) + if !ok { + return errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) + } + oldNames := image.Names + names, err := applyNameOperation(oldNames, names, op) + if err != nil { + return err + } + for _, name := range oldNames { + delete(r.byname, name) + } + for _, name := range names { + if otherImage, ok := r.byname[name]; ok { + r.removeName(otherImage, name) } - image.Names = names - return r.Save() + r.byname[name] = image + image.addNameToHistory(name) } - return errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) + image.Names = names + return r.Save() } func (r *imageStore) Delete(id string) error { diff --git a/vendor/github.com/containers/storage/layers.go b/vendor/github.com/containers/storage/layers.go index e2357c540..8a5616dfc 100644 --- a/vendor/github.com/containers/storage/layers.go +++ b/vendor/github.com/containers/storage/layers.go @@ -221,8 +221,17 @@ type LayerStore interface { // SetNames replaces the list of names associated with a layer with the // supplied values. + // Deprecated: Prone to race conditions, suggested alternatives are `AddNames` and `RemoveNames`. SetNames(id string, names []string) error + // AddNames adds the supplied values to the list of names associated with the layer with the + // specified id. + AddNames(id string, names []string) error + + // RemoveNames remove the supplied values from the list of names associated with the layer with the + // specified id. + RemoveNames(id string, names []string) error + // Delete deletes a layer with the specified name or ID. Delete(id string) error @@ -399,14 +408,13 @@ func (r *layerStore) Load() error { if layer.Flags == nil { layer.Flags = make(map[string]interface{}) } - if cleanup, ok := layer.Flags[incompleteFlag]; ok { - if b, ok := cleanup.(bool); ok && b { - err = r.deleteInternal(layer.ID) - if err != nil { - break - } - shouldSave = true + if layerHasIncompleteFlag(layer) { + logrus.Warnf("Found incomplete layer %#v, deleting it", layer.ID) + err = r.deleteInternal(layer.ID) + if err != nil { + break } + shouldSave = true } } } @@ -742,26 +750,17 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab } if moreOptions.TemplateLayer != "" { if err = r.driver.CreateFromTemplate(id, moreOptions.TemplateLayer, templateIDMappings, parent, parentMappings, &opts, writeable); err != nil { - if id != "" { - return nil, -1, errors.Wrapf(err, "error creating copy of template layer %q with ID %q", moreOptions.TemplateLayer, id) - } - return nil, -1, errors.Wrapf(err, "error creating copy of template layer %q", moreOptions.TemplateLayer) + return nil, -1, errors.Wrapf(err, "error creating copy of template layer %q with ID %q", moreOptions.TemplateLayer, id) } oldMappings = templateIDMappings } else { if writeable { if err = r.driver.CreateReadWrite(id, parent, &opts); err != nil { - if id != "" { - return nil, -1, errors.Wrapf(err, "error creating read-write layer with ID %q", id) - } - return nil, -1, errors.Wrapf(err, "error creating read-write layer") + return nil, -1, errors.Wrapf(err, "error creating read-write layer with ID %q", id) } } else { if err = r.driver.Create(id, parent, &opts); err != nil { - if id != "" { - return nil, -1, errors.Wrapf(err, "error creating layer with ID %q", id) - } - return nil, -1, errors.Wrapf(err, "error creating layer") + return nil, -1, errors.Wrapf(err, "error creating layer with ID %q", id) } } oldMappings = parentMappings @@ -770,7 +769,9 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab if err = r.driver.UpdateLayerIDMap(id, oldMappings, idMappings, mountLabel); err != nil { // We don't have a record of this layer, but at least // try to clean it up underneath us. - r.driver.Remove(id) + if err2 := r.driver.Remove(id); err2 != nil { + logrus.Errorf("While recovering from a failure creating in UpdateLayerIDMap, error deleting layer %#v: %v", id, err2) + } return nil, -1, err } } @@ -795,21 +796,26 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab for flag, value := range flags { layer.Flags[flag] = value } + savedIncompleteLayer := false if diff != nil { layer.Flags[incompleteFlag] = true err = r.Save() if err != nil { // We don't have a record of this layer, but at least // try to clean it up underneath us. - r.driver.Remove(id) + if err2 := r.driver.Remove(id); err2 != nil { + logrus.Errorf("While recovering from a failure saving incomplete layer metadata, error deleting layer %#v: %v", id, err2) + } return nil, -1, err } + savedIncompleteLayer = true size, err = r.applyDiffWithOptions(layer.ID, moreOptions, diff) if err != nil { - if r.Delete(layer.ID) != nil { + if err2 := r.Delete(layer.ID); err2 != nil { // Either a driver error or an error saving. // We now have a layer that's been marked for // deletion but which we failed to remove. + logrus.Errorf("While recovering from a failure applying layer diff, error deleting layer %#v: %v", layer.ID, err2) } return nil, -1, err } @@ -817,9 +823,20 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab } err = r.Save() if err != nil { - // We don't have a record of this layer, but at least - // try to clean it up underneath us. - r.driver.Remove(id) + if savedIncompleteLayer { + if err2 := r.Delete(layer.ID); err2 != nil { + // Either a driver error or an error saving. + // We now have a layer that's been marked for + // deletion but which we failed to remove. + logrus.Errorf("While recovering from a failure saving finished layer metadata, error deleting layer %#v: %v", layer.ID, err2) + } + } else { + // We don't have a record of this layer, but at least + // try to clean it up underneath us. + if err2 := r.driver.Remove(id); err2 != nil { + logrus.Errorf("While recovering from a failure saving finished layer metadata, error deleting layer %#v in graph driver: %v", id, err2) + } + } return nil, -1, err } layer = copyLayer(layer) @@ -1032,25 +1049,43 @@ func (r *layerStore) removeName(layer *Layer, name string) { layer.Names = stringSliceWithoutValue(layer.Names, name) } +// Deprecated: Prone to race conditions, suggested alternatives are `AddNames` and `RemoveNames`. func (r *layerStore) SetNames(id string, names []string) error { + return r.updateNames(id, names, setNames) +} + +func (r *layerStore) AddNames(id string, names []string) error { + return r.updateNames(id, names, addNames) +} + +func (r *layerStore) RemoveNames(id string, names []string) error { + return r.updateNames(id, names, removeNames) +} + +func (r *layerStore) updateNames(id string, names []string, op updateNameOperation) error { if !r.IsReadWrite() { return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to change layer name assignments at %q", r.layerspath()) } - names = dedupeNames(names) - if layer, ok := r.lookup(id); ok { - for _, name := range layer.Names { - delete(r.byname, name) - } - for _, name := range names { - if otherLayer, ok := r.byname[name]; ok { - r.removeName(otherLayer, name) - } - r.byname[name] = layer + layer, ok := r.lookup(id) + if !ok { + return ErrLayerUnknown + } + oldNames := layer.Names + names, err := applyNameOperation(oldNames, names, op) + if err != nil { + return err + } + for _, name := range oldNames { + delete(r.byname, name) + } + for _, name := range names { + if otherLayer, ok := r.byname[name]; ok { + r.removeName(otherLayer, name) } - layer.Names = names - return r.Save() + r.byname[name] = layer } - return ErrLayerUnknown + layer.Names = names + return r.Save() } func (r *layerStore) datadir(id string) string { @@ -1149,6 +1184,17 @@ func (r *layerStore) tspath(id string) string { return filepath.Join(r.layerdir, id+tarSplitSuffix) } +// layerHasIncompleteFlag returns true if layer.Flags contains an incompleteFlag set to true +func layerHasIncompleteFlag(layer *Layer) bool { + // layer.Flags[…] is defined to succeed and return ok == false if Flags == nil + if flagValue, ok := layer.Flags[incompleteFlag]; ok { + if b, ok := flagValue.(bool); ok && b { + return true + } + } + return false +} + func (r *layerStore) deleteInternal(id string) error { if !r.IsReadWrite() { return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to delete layers at %q", r.layerspath()) @@ -1157,6 +1203,18 @@ func (r *layerStore) deleteInternal(id string) error { if !ok { return ErrLayerUnknown } + // Ensure that if we are interrupted, the layer will be cleaned up. + if !layerHasIncompleteFlag(layer) { + if layer.Flags == nil { + layer.Flags = make(map[string]interface{}) + } + layer.Flags[incompleteFlag] = true + if err := r.Save(); err != nil { + return err + } + } + // We never unset incompleteFlag; below, we remove the entire object from r.layers. + id = layer.ID err := r.driver.Remove(id) if err != nil { diff --git a/vendor/github.com/containers/storage/pkg/chunked/storage_linux.go b/vendor/github.com/containers/storage/pkg/chunked/storage_linux.go index 92b15c2bf..7de20feaa 100644 --- a/vendor/github.com/containers/storage/pkg/chunked/storage_linux.go +++ b/vendor/github.com/containers/storage/pkg/chunked/storage_linux.go @@ -1248,7 +1248,7 @@ func (d whiteoutHandler) Mknod(path string, mode uint32, dev int) error { func checkChownErr(err error, name string, uid, gid int) error { if errors.Is(err, syscall.EINVAL) { - return fmt.Errorf("potentially insufficient UIDs or GIDs available in user namespace (requested %d:%d for %s): Check /etc/subuid and /etc/subgid if configured locally: %w", uid, gid, name, err) + return fmt.Errorf("potentially insufficient UIDs or GIDs available in user namespace (requested %d:%d for %s): Check /etc/subuid and /etc/subgid if configured locally and run podman-system-migrate: %w", uid, gid, name, err) } return err } diff --git a/vendor/github.com/containers/storage/pkg/idtools/idtools.go b/vendor/github.com/containers/storage/pkg/idtools/idtools.go index 0abe886eb..a19ba288b 100644 --- a/vendor/github.com/containers/storage/pkg/idtools/idtools.go +++ b/vendor/github.com/containers/storage/pkg/idtools/idtools.go @@ -297,7 +297,7 @@ func parseSubidFile(path, username string) (ranges, error) { func checkChownErr(err error, name string, uid, gid int) error { if e, ok := err.(*os.PathError); ok && e.Err == syscall.EINVAL { - return errors.Wrapf(err, "potentially insufficient UIDs or GIDs available in user namespace (requested %d:%d for %s): Check /etc/subuid and /etc/subgid if configured locally", uid, gid, name) + return errors.Wrapf(err, "potentially insufficient UIDs or GIDs available in user namespace (requested %d:%d for %s): Check /etc/subuid and /etc/subgid if configured locally and run podman-system-migrate", uid, gid, name) } return err } diff --git a/vendor/github.com/containers/storage/store.go b/vendor/github.com/containers/storage/store.go index 062ce6fb7..6b40b68ca 100644 --- a/vendor/github.com/containers/storage/store.go +++ b/vendor/github.com/containers/storage/store.go @@ -31,6 +31,14 @@ import ( "github.com/pkg/errors" ) +type updateNameOperation int + +const ( + setNames updateNameOperation = iota + addNames + removeNames +) + var ( stores []*store storesLock sync.Mutex @@ -368,8 +376,17 @@ type Store interface { // SetNames changes the list of names for a layer, image, or container. // Duplicate names are removed from the list automatically. + // Deprecated: Prone to race conditions, suggested alternatives are `AddNames` and `RemoveNames`. SetNames(id string, names []string) error + // AddNames adds the list of names for a layer, image, or container. + // Duplicate names are removed from the list automatically. + AddNames(id string, names []string) error + + // RemoveNames removes the list of names for a layer, image, or container. + // Duplicate names are removed from the list automatically. + RemoveNames(id string, names []string) error + // ListImageBigData retrieves a list of the (possibly large) chunks of // named data associated with an image. ListImageBigData(id string) ([]string, error) @@ -2050,7 +2067,20 @@ func dedupeNames(names []string) []string { return deduped } +// Deprecated: Prone to race conditions, suggested alternatives are `AddNames` and `RemoveNames`. func (s *store) SetNames(id string, names []string) error { + return s.updateNames(id, names, setNames) +} + +func (s *store) AddNames(id string, names []string) error { + return s.updateNames(id, names, addNames) +} + +func (s *store) RemoveNames(id string, names []string) error { + return s.updateNames(id, names, removeNames) +} + +func (s *store) updateNames(id string, names []string, op updateNameOperation) error { deduped := dedupeNames(names) rlstore, err := s.LayerStore() @@ -2063,7 +2093,16 @@ func (s *store) SetNames(id string, names []string) error { return err } if rlstore.Exists(id) { - return rlstore.SetNames(id, deduped) + switch op { + case setNames: + return rlstore.SetNames(id, deduped) + case removeNames: + return rlstore.RemoveNames(id, deduped) + case addNames: + return rlstore.AddNames(id, deduped) + default: + return errInvalidUpdateNameOperation + } } ristore, err := s.ImageStore() @@ -2076,7 +2115,16 @@ func (s *store) SetNames(id string, names []string) error { return err } if ristore.Exists(id) { - return ristore.SetNames(id, deduped) + switch op { + case setNames: + return ristore.SetNames(id, deduped) + case removeNames: + return ristore.RemoveNames(id, deduped) + case addNames: + return ristore.AddNames(id, deduped) + default: + return errInvalidUpdateNameOperation + } } // Check is id refers to a RO Store @@ -2114,7 +2162,16 @@ func (s *store) SetNames(id string, names []string) error { return err } if rcstore.Exists(id) { - return rcstore.SetNames(id, deduped) + switch op { + case setNames: + return rcstore.SetNames(id, deduped) + case removeNames: + return rcstore.RemoveNames(id, deduped) + case addNames: + return rcstore.AddNames(id, deduped) + default: + return errInvalidUpdateNameOperation + } } return ErrLayerUnknown } @@ -2532,17 +2589,12 @@ func (s *store) DeleteContainer(id string) error { }() var errors []error - for { - select { - case err, ok := <-errChan: - if !ok { - return multierror.Append(nil, errors...).ErrorOrNil() - } - if err != nil { - errors = append(errors, err) - } + for err := range errChan { + if err != nil { + errors = append(errors, err) } } + return multierror.Append(nil, errors...).ErrorOrNil() } } return ErrNotAContainer diff --git a/vendor/github.com/containers/storage/utils.go b/vendor/github.com/containers/storage/utils.go index 80d56041b..cec377f26 100644 --- a/vendor/github.com/containers/storage/utils.go +++ b/vendor/github.com/containers/storage/utils.go @@ -40,3 +40,35 @@ func validateMountOptions(mountOptions []string) error { } return nil } + +func applyNameOperation(oldNames []string, opParameters []string, op updateNameOperation) ([]string, error) { + result := make([]string, 0) + switch op { + case setNames: + // ignore all old names and just return new names + return dedupeNames(opParameters), nil + case removeNames: + // remove given names from old names + for _, name := range oldNames { + // only keep names in final result which do not intersect with input names + // basically `result = oldNames - opParameters` + nameShouldBeRemoved := false + for _, opName := range opParameters { + if name == opName { + nameShouldBeRemoved = true + } + } + if !nameShouldBeRemoved { + result = append(result, name) + } + } + return dedupeNames(result), nil + case addNames: + result = append(result, opParameters...) + result = append(result, oldNames...) + return dedupeNames(result), nil + default: + return result, errInvalidUpdateNameOperation + } + return dedupeNames(result), nil +} diff --git a/vendor/github.com/docker/distribution/.mailmap b/vendor/github.com/docker/distribution/.mailmap index 34421a4ec..8f3738f3d 100644 --- a/vendor/github.com/docker/distribution/.mailmap +++ b/vendor/github.com/docker/distribution/.mailmap @@ -44,3 +44,6 @@ Thomas Berger <loki@lokis-chaos.de> Thomas Berger <tbe@users.noreply.github.com> Samuel Karp <skarp@amazon.com> Samuel Karp <samuelkarp@users.noreply.github.com> Justin Cormack <justin.cormack@docker.com> sayboras <sayboras@yahoo.com> +CrazyMax <github@crazymax.dev> +CrazyMax <github@crazymax.dev> <1951866+crazy-max@users.noreply.github.com> +CrazyMax <github@crazymax.dev> <crazy-max@users.noreply.github.com> diff --git a/vendor/github.com/docker/distribution/Dockerfile b/vendor/github.com/docker/distribution/Dockerfile index 9d30d3771..ae8c040c7 100644 --- a/vendor/github.com/docker/distribution/Dockerfile +++ b/vendor/github.com/docker/distribution/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.3 -ARG GO_VERSION=1.16 +ARG GO_VERSION=1.16.15 ARG GORELEASER_XX_VERSION=1.2.5 FROM --platform=$BUILDPLATFORM crazymax/goreleaser-xx:${GORELEASER_XX_VERSION} AS goreleaser-xx @@ -12,6 +12,10 @@ WORKDIR /go/src/github.com/docker/distribution FROM base AS build ENV GO111MODULE=auto ENV CGO_ENABLED=0 +# GIT_REF is used by goreleaser-xx to handle the proper git ref when available. +# It will fallback to the working tree info if empty and use "git tag --points-at" +# or "git describe" to define the version info. +ARG GIT_REF ARG TARGETPLATFORM ARG PKG="github.com/distribution/distribution" ARG BUILDTAGS="include_oss include_gcs" @@ -28,7 +32,7 @@ RUN --mount=type=bind,rw \ --files="LICENSE" \ --files="README.md" -FROM scratch AS artifacts +FROM scratch AS artifact COPY --from=build /out/*.tar.gz / COPY --from=build /out/*.zip / COPY --from=build /out/*.sha256 / diff --git a/vendor/github.com/docker/distribution/README.md b/vendor/github.com/docker/distribution/README.md index 998878850..e513c18e9 100644 --- a/vendor/github.com/docker/distribution/README.md +++ b/vendor/github.com/docker/distribution/README.md @@ -2,7 +2,7 @@ The Docker toolset to pack, ship, store, and deliver content. -This repository's main product is the Docker Registry 2.0 implementation +This repository provides the Docker Registry 2.0 implementation for storing and distributing Docker images. It supersedes the [docker/docker-registry](https://github.com/docker/docker-registry) project with a new API design, focused around security and performance. diff --git a/vendor/github.com/docker/distribution/docker-bake.hcl b/vendor/github.com/docker/distribution/docker-bake.hcl index e1457bb81..4dd5a100c 100644 --- a/vendor/github.com/docker/distribution/docker-bake.hcl +++ b/vendor/github.com/docker/distribution/docker-bake.hcl @@ -1,3 +1,15 @@ +// GITHUB_REF is the actual ref that triggers the workflow +// https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables +variable "GITHUB_REF" { + default = "" +} + +target "_common" { + args = { + GIT_REF = GITHUB_REF + } +} + group "default" { targets = ["image-local"] } @@ -8,12 +20,14 @@ target "docker-metadata-action" { } target "binary" { + inherits = ["_common"] target = "binary" output = ["./bin"] } target "artifact" { - target = "artifacts" + inherits = ["_common"] + target = "artifact" output = ["./bin"] } @@ -30,7 +44,7 @@ target "artifact-all" { } target "image" { - inherits = ["docker-metadata-action"] + inherits = ["_common", "docker-metadata-action"] } target "image-local" { diff --git a/vendor/github.com/vbauerster/mpb/v6/.gitignore b/vendor/github.com/vbauerster/mpb/v6/.gitignore deleted file mode 100644 index 63bd91672..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# Test binary, build with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out diff --git a/vendor/github.com/vbauerster/mpb/v6/.travis.yml b/vendor/github.com/vbauerster/mpb/v6/.travis.yml deleted file mode 100644 index 9a203a67d..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -language: go -arch: - - amd64 - - ppc64le - -go: - - 1.14.x - -script: - - go test -race ./... - - for i in _examples/*/; do go build $i/*.go || exit 1; done diff --git a/vendor/github.com/vbauerster/mpb/v6/README.md b/vendor/github.com/vbauerster/mpb/v6/README.md deleted file mode 100644 index a87786d72..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/README.md +++ /dev/null @@ -1,120 +0,0 @@ -# Multi Progress Bar - -[![GoDoc](https://pkg.go.dev/badge/github.com/vbauerster/mpb)](https://pkg.go.dev/github.com/vbauerster/mpb/v6) -[![Build Status](https://travis-ci.org/vbauerster/mpb.svg?branch=master)](https://travis-ci.org/vbauerster/mpb) -[![Go Report Card](https://goreportcard.com/badge/github.com/vbauerster/mpb)](https://goreportcard.com/report/github.com/vbauerster/mpb) - -**mpb** is a Go lib for rendering progress bars in terminal applications. - -## Features - -- **Multiple Bars**: Multiple progress bars are supported -- **Dynamic Total**: Set total while bar is running -- **Dynamic Add/Remove**: Dynamically add or remove bars -- **Cancellation**: Cancel whole rendering process -- **Predefined Decorators**: Elapsed time, [ewma](https://github.com/VividCortex/ewma) based ETA, Percentage, Bytes counter -- **Decorator's width sync**: Synchronized decorator's width among multiple bars - -## Usage - -#### [Rendering single bar](_examples/singleBar/main.go) - -```go -package main - -import ( - "math/rand" - "time" - - "github.com/vbauerster/mpb/v6" - "github.com/vbauerster/mpb/v6/decor" -) - -func main() { - // initialize progress container, with custom width - p := mpb.New(mpb.WithWidth(64)) - - total := 100 - name := "Single Bar:" - // adding a single bar, which will inherit container's width - bar := p.Add(int64(total), - // progress bar filler with customized style - mpb.NewBarFiller("╢▌▌░╟"), - mpb.PrependDecorators( - // display our name with one space on the right - decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}), - // replace ETA decorator with "done" message, OnComplete event - decor.OnComplete( - decor.AverageETA(decor.ET_STYLE_GO, decor.WC{W: 4}), "done", - ), - ), - mpb.AppendDecorators(decor.Percentage()), - ) - // simulating some work - max := 100 * time.Millisecond - for i := 0; i < total; i++ { - time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) - bar.Increment() - } - // wait for our bar to complete and flush - p.Wait() -} -``` - -#### [Rendering multiple bars](_examples/multiBars/main.go) - -```go - var wg sync.WaitGroup - // pass &wg (optional), so p will wait for it eventually - p := mpb.New(mpb.WithWaitGroup(&wg)) - total, numBars := 100, 3 - wg.Add(numBars) - - for i := 0; i < numBars; i++ { - name := fmt.Sprintf("Bar#%d:", i) - bar := p.AddBar(int64(total), - mpb.PrependDecorators( - // simple name decorator - decor.Name(name), - // decor.DSyncWidth bit enables column width synchronization - decor.Percentage(decor.WCSyncSpace), - ), - mpb.AppendDecorators( - // replace ETA decorator with "done" message, OnComplete event - decor.OnComplete( - // ETA decorator with ewma age of 60 - decor.EwmaETA(decor.ET_STYLE_GO, 60), "done", - ), - ), - ) - // simulating some work - go func() { - defer wg.Done() - rng := rand.New(rand.NewSource(time.Now().UnixNano())) - max := 100 * time.Millisecond - for i := 0; i < total; i++ { - // start variable is solely for EWMA calculation - // EWMA's unit of measure is an iteration's duration - start := time.Now() - time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10) - bar.Increment() - // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract - bar.DecoratorEwmaUpdate(time.Since(start)) - } - }() - } - // Waiting for passed &wg and for all bars to complete and flush - p.Wait() -``` - -#### [Dynamic total](_examples/dynTotal/main.go) - -![dynamic total](_svg/godEMrCZmJkHYH1X9dN4Nm0U7.svg) - -#### [Complex example](_examples/complex/main.go) - -![complex](_svg/wHzf1M7sd7B3zVa2scBMnjqRf.svg) - -#### [Bytes counters](_examples/io/main.go) - -![byte counters](_svg/hIpTa3A5rQz65ssiVuRJu87X6.svg) diff --git a/vendor/github.com/vbauerster/mpb/v6/UNLICENSE b/vendor/github.com/vbauerster/mpb/v6/UNLICENSE deleted file mode 100644 index 68a49daad..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/UNLICENSE +++ /dev/null @@ -1,24 +0,0 @@ -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any -means. - -In jurisdictions that recognize copyright laws, the author or authors -of this software dedicate any and all copyright interest in the -software to the public domain. We make this dedication for the benefit -of the public at large and to the detriment of our heirs and -successors. We intend this dedication to be an overt act of -relinquishment in perpetuity of all present and future rights to this -software under copyright law. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -For more information, please refer to <http://unlicense.org/> diff --git a/vendor/github.com/vbauerster/mpb/v6/bar.go b/vendor/github.com/vbauerster/mpb/v6/bar.go deleted file mode 100644 index f18ef967f..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/bar.go +++ /dev/null @@ -1,492 +0,0 @@ -package mpb - -import ( - "bytes" - "context" - "fmt" - "io" - "log" - "runtime/debug" - "strings" - "time" - - "github.com/acarl005/stripansi" - "github.com/mattn/go-runewidth" - "github.com/vbauerster/mpb/v6/decor" -) - -// Bar represents a progress bar. -type Bar struct { - priority int // used by heap - index int // used by heap - - extendedLines int - toShutdown bool - toDrop bool - noPop bool - hasEwmaDecorators bool - operateState chan func(*bState) - frameCh chan io.Reader - syncTableCh chan [][]chan int - completed chan bool - - // cancel is called either by user or on complete event - cancel func() - // done is closed after cacheState is assigned - done chan struct{} - // cacheState is populated, right after close(shutdown) - cacheState *bState - - container *Progress - dlogger *log.Logger - recoveredPanic interface{} -} - -type extenderFunc func(in io.Reader, reqWidth int, st decor.Statistics) (out io.Reader, lines int) - -// bState is actual bar state. It gets passed to *Bar.serve(...) monitor -// goroutine. -type bState struct { - id int - priority int - reqWidth int - total int64 - current int64 - refill int64 - lastN int64 - iterated bool - trimSpace bool - completed bool - completeFlushed bool - triggerComplete bool - dropOnComplete bool - noPop bool - aDecorators []decor.Decorator - pDecorators []decor.Decorator - averageDecorators []decor.AverageDecorator - ewmaDecorators []decor.EwmaDecorator - shutdownListeners []decor.ShutdownListener - bufP, bufB, bufA *bytes.Buffer - filler BarFiller - middleware func(BarFiller) BarFiller - extender extenderFunc - - // runningBar is a key for *pState.parkedBars - runningBar *Bar - - debugOut io.Writer -} - -func newBar(container *Progress, bs *bState) *Bar { - logPrefix := fmt.Sprintf("%sbar#%02d ", container.dlogger.Prefix(), bs.id) - ctx, cancel := context.WithCancel(container.ctx) - - bar := &Bar{ - container: container, - priority: bs.priority, - toDrop: bs.dropOnComplete, - noPop: bs.noPop, - operateState: make(chan func(*bState)), - frameCh: make(chan io.Reader, 1), - syncTableCh: make(chan [][]chan int, 1), - completed: make(chan bool, 1), - done: make(chan struct{}), - cancel: cancel, - dlogger: log.New(bs.debugOut, logPrefix, log.Lshortfile), - } - - go bar.serve(ctx, bs) - return bar -} - -// ProxyReader wraps r with metrics required for progress tracking. -// Panics if r is nil. -func (b *Bar) ProxyReader(r io.Reader) io.ReadCloser { - if r == nil { - panic("expected non nil io.Reader") - } - return newProxyReader(r, b) -} - -// ID returs id of the bar. -func (b *Bar) ID() int { - result := make(chan int) - select { - case b.operateState <- func(s *bState) { result <- s.id }: - return <-result - case <-b.done: - return b.cacheState.id - } -} - -// Current returns bar's current number, in other words sum of all increments. -func (b *Bar) Current() int64 { - result := make(chan int64) - select { - case b.operateState <- func(s *bState) { result <- s.current }: - return <-result - case <-b.done: - return b.cacheState.current - } -} - -// SetRefill sets refill flag with specified amount. -// The underlying BarFiller will change its visual representation, to -// indicate refill event. Refill event may be referred to some retry -// operation for example. -func (b *Bar) SetRefill(amount int64) { - select { - case b.operateState <- func(s *bState) { - s.refill = amount - }: - case <-b.done: - } -} - -// TraverseDecorators traverses all available decorators and calls cb func on each. -func (b *Bar) TraverseDecorators(cb func(decor.Decorator)) { - select { - case b.operateState <- func(s *bState) { - for _, decorators := range [...][]decor.Decorator{ - s.pDecorators, - s.aDecorators, - } { - for _, d := range decorators { - cb(extractBaseDecorator(d)) - } - } - }: - case <-b.done: - } -} - -// SetTotal sets total dynamically. -// If total is less than or equal to zero it takes progress' current value. -func (b *Bar) SetTotal(total int64, triggerComplete bool) { - select { - case b.operateState <- func(s *bState) { - s.triggerComplete = triggerComplete - if total <= 0 { - s.total = s.current - } else { - s.total = total - } - if s.triggerComplete && !s.completed { - s.current = s.total - s.completed = true - go b.refreshTillShutdown() - } - }: - case <-b.done: - } -} - -// SetCurrent sets progress' current to an arbitrary value. -// Setting a negative value will cause a panic. -func (b *Bar) SetCurrent(current int64) { - select { - case b.operateState <- func(s *bState) { - s.iterated = true - s.lastN = current - s.current - s.current = current - if s.triggerComplete && s.current >= s.total { - s.current = s.total - s.completed = true - go b.refreshTillShutdown() - } - }: - case <-b.done: - } -} - -// Increment is a shorthand for b.IncrInt64(1). -func (b *Bar) Increment() { - b.IncrInt64(1) -} - -// IncrBy is a shorthand for b.IncrInt64(int64(n)). -func (b *Bar) IncrBy(n int) { - b.IncrInt64(int64(n)) -} - -// IncrInt64 increments progress by amount of n. -func (b *Bar) IncrInt64(n int64) { - select { - case b.operateState <- func(s *bState) { - s.iterated = true - s.lastN = n - s.current += n - if s.triggerComplete && s.current >= s.total { - s.current = s.total - s.completed = true - go b.refreshTillShutdown() - } - }: - case <-b.done: - } -} - -// DecoratorEwmaUpdate updates all EWMA based decorators. Should be -// called on each iteration, because EWMA's unit of measure is an -// iteration's duration. Panics if called before *Bar.Incr... family -// methods. -func (b *Bar) DecoratorEwmaUpdate(dur time.Duration) { - select { - case b.operateState <- func(s *bState) { - ewmaIterationUpdate(false, s, dur) - }: - case <-b.done: - ewmaIterationUpdate(true, b.cacheState, dur) - } -} - -// DecoratorAverageAdjust adjusts all average based decorators. Call -// if you need to adjust start time of all average based decorators -// or after progress resume. -func (b *Bar) DecoratorAverageAdjust(start time.Time) { - select { - case b.operateState <- func(s *bState) { - for _, d := range s.averageDecorators { - d.AverageAdjust(start) - } - }: - case <-b.done: - } -} - -// SetPriority changes bar's order among multiple bars. Zero is highest -// priority, i.e. bar will be on top. If you don't need to set priority -// dynamically, better use BarPriority option. -func (b *Bar) SetPriority(priority int) { - select { - case <-b.done: - default: - b.container.setBarPriority(b, priority) - } -} - -// Abort interrupts bar's running goroutine. Call this, if you'd like -// to stop/remove bar before completion event. It has no effect after -// completion event. If drop is true bar will be removed as well. -func (b *Bar) Abort(drop bool) { - select { - case <-b.done: - default: - if drop { - b.container.dropBar(b) - } - b.cancel() - } -} - -// Completed reports whether the bar is in completed state. -func (b *Bar) Completed() bool { - select { - case b.operateState <- func(s *bState) { b.completed <- s.completed }: - return <-b.completed - case <-b.done: - return true - } -} - -func (b *Bar) serve(ctx context.Context, s *bState) { - defer b.container.bwg.Done() - for { - select { - case op := <-b.operateState: - op(s) - case <-ctx.Done(): - b.cacheState = s - close(b.done) - // Notifying decorators about shutdown event - for _, sl := range s.shutdownListeners { - sl.Shutdown() - } - return - } - } -} - -func (b *Bar) render(tw int) { - select { - case b.operateState <- func(s *bState) { - stat := newStatistics(tw, s) - defer func() { - // recovering if user defined decorator panics for example - if p := recover(); p != nil { - if b.recoveredPanic == nil { - s.extender = makePanicExtender(p) - b.toShutdown = !b.toShutdown - b.recoveredPanic = p - } - frame, lines := s.extender(nil, s.reqWidth, stat) - b.extendedLines = lines - b.frameCh <- frame - b.dlogger.Println(p) - } - s.completeFlushed = s.completed - }() - frame, lines := s.extender(s.draw(stat), s.reqWidth, stat) - b.extendedLines = lines - b.toShutdown = s.completed && !s.completeFlushed - b.frameCh <- frame - }: - case <-b.done: - s := b.cacheState - stat := newStatistics(tw, s) - var r io.Reader - if b.recoveredPanic == nil { - r = s.draw(stat) - } - frame, lines := s.extender(r, s.reqWidth, stat) - b.extendedLines = lines - b.frameCh <- frame - } -} - -func (b *Bar) subscribeDecorators() { - var averageDecorators []decor.AverageDecorator - var ewmaDecorators []decor.EwmaDecorator - var shutdownListeners []decor.ShutdownListener - b.TraverseDecorators(func(d decor.Decorator) { - if d, ok := d.(decor.AverageDecorator); ok { - averageDecorators = append(averageDecorators, d) - } - if d, ok := d.(decor.EwmaDecorator); ok { - ewmaDecorators = append(ewmaDecorators, d) - } - if d, ok := d.(decor.ShutdownListener); ok { - shutdownListeners = append(shutdownListeners, d) - } - }) - select { - case b.operateState <- func(s *bState) { - s.averageDecorators = averageDecorators - s.ewmaDecorators = ewmaDecorators - s.shutdownListeners = shutdownListeners - }: - b.hasEwmaDecorators = len(ewmaDecorators) != 0 - case <-b.done: - } -} - -func (b *Bar) refreshTillShutdown() { - for { - select { - case b.container.refreshCh <- time.Now(): - case <-b.done: - return - } - } -} - -func (b *Bar) wSyncTable() [][]chan int { - select { - case b.operateState <- func(s *bState) { b.syncTableCh <- s.wSyncTable() }: - return <-b.syncTableCh - case <-b.done: - return b.cacheState.wSyncTable() - } -} - -func (s *bState) draw(stat decor.Statistics) io.Reader { - if !s.trimSpace { - stat.AvailableWidth -= 2 - s.bufB.WriteByte(' ') - defer s.bufB.WriteByte(' ') - } - - nlr := strings.NewReader("\n") - tw := stat.AvailableWidth - for _, d := range s.pDecorators { - str := d.Decor(stat) - stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str)) - s.bufP.WriteString(str) - } - if stat.AvailableWidth <= 0 { - trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(s.bufP.String()), tw, "…")) - s.bufP.Reset() - return io.MultiReader(trunc, s.bufB, nlr) - } - - tw = stat.AvailableWidth - for _, d := range s.aDecorators { - str := d.Decor(stat) - stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str)) - s.bufA.WriteString(str) - } - if stat.AvailableWidth <= 0 { - trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(s.bufA.String()), tw, "…")) - s.bufA.Reset() - return io.MultiReader(s.bufP, s.bufB, trunc, nlr) - } - - s.filler.Fill(s.bufB, s.reqWidth, stat) - - return io.MultiReader(s.bufP, s.bufB, s.bufA, nlr) -} - -func (s *bState) wSyncTable() [][]chan int { - columns := make([]chan int, 0, len(s.pDecorators)+len(s.aDecorators)) - var pCount int - for _, d := range s.pDecorators { - if ch, ok := d.Sync(); ok { - columns = append(columns, ch) - pCount++ - } - } - var aCount int - for _, d := range s.aDecorators { - if ch, ok := d.Sync(); ok { - columns = append(columns, ch) - aCount++ - } - } - table := make([][]chan int, 2) - table[0] = columns[0:pCount] - table[1] = columns[pCount : pCount+aCount : pCount+aCount] - return table -} - -func newStatistics(tw int, s *bState) decor.Statistics { - return decor.Statistics{ - ID: s.id, - AvailableWidth: tw, - Total: s.total, - Current: s.current, - Refill: s.refill, - Completed: s.completeFlushed, - } -} - -func extractBaseDecorator(d decor.Decorator) decor.Decorator { - if d, ok := d.(decor.Wrapper); ok { - return extractBaseDecorator(d.Base()) - } - return d -} - -func ewmaIterationUpdate(done bool, s *bState, dur time.Duration) { - if !done && !s.iterated { - panic("increment required before ewma iteration update") - } else { - s.iterated = false - } - for _, d := range s.ewmaDecorators { - d.EwmaUpdate(s.lastN, dur) - } -} - -func makePanicExtender(p interface{}) extenderFunc { - pstr := fmt.Sprint(p) - stack := debug.Stack() - stackLines := bytes.Count(stack, []byte("\n")) - return func(_ io.Reader, _ int, st decor.Statistics) (io.Reader, int) { - mr := io.MultiReader( - strings.NewReader(runewidth.Truncate(pstr, st.AvailableWidth, "…")), - strings.NewReader(fmt.Sprintf("\n%#v\n", st)), - bytes.NewReader(stack), - ) - return mr, stackLines + 1 - } -} diff --git a/vendor/github.com/vbauerster/mpb/v6/bar_filler.go b/vendor/github.com/vbauerster/mpb/v6/bar_filler.go deleted file mode 100644 index c8cedaa4a..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/bar_filler.go +++ /dev/null @@ -1,31 +0,0 @@ -package mpb - -import ( - "io" - - "github.com/vbauerster/mpb/v6/decor" -) - -// BarFiller interface. -// Bar (without decorators) renders itself by calling BarFiller's Fill method. -// -// reqWidth is requested width, set by `func WithWidth(int) ContainerOption`. -// If not set, it defaults to terminal width. -// -// Default implementations can be obtained via: -// -// func NewBarFiller(style string) BarFiller -// func NewBarFillerRev(style string) BarFiller -// func NewBarFillerPick(style string, rev bool) BarFiller -// func NewSpinnerFiller(style []string, alignment SpinnerAlignment) BarFiller -// -type BarFiller interface { - Fill(w io.Writer, reqWidth int, stat decor.Statistics) -} - -// BarFillerFunc is function type adapter to convert function into BarFiller. -type BarFillerFunc func(w io.Writer, reqWidth int, stat decor.Statistics) - -func (f BarFillerFunc) Fill(w io.Writer, reqWidth int, stat decor.Statistics) { - f(w, reqWidth, stat) -} diff --git a/vendor/github.com/vbauerster/mpb/v6/bar_filler_bar.go b/vendor/github.com/vbauerster/mpb/v6/bar_filler_bar.go deleted file mode 100644 index 1c339e91d..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/bar_filler_bar.go +++ /dev/null @@ -1,191 +0,0 @@ -package mpb - -import ( - "bytes" - "io" - "unicode/utf8" - - "github.com/mattn/go-runewidth" - "github.com/rivo/uniseg" - "github.com/vbauerster/mpb/v6/decor" - "github.com/vbauerster/mpb/v6/internal" -) - -const ( - rLeft = iota - rFill - rTip - rSpace - rRight - rRevTip - rRefill -) - -// BarDefaultStyle is a style for rendering a progress bar. -// It consist of 7 ordered runes: -// -// '1st rune' stands for left boundary rune -// -// '2nd rune' stands for fill rune -// -// '3rd rune' stands for tip rune -// -// '4th rune' stands for space rune -// -// '5th rune' stands for right boundary rune -// -// '6th rune' stands for reverse tip rune -// -// '7th rune' stands for refill rune -// -const BarDefaultStyle string = "[=>-]<+" - -type barFiller struct { - format [][]byte - rwidth []int - tip []byte - refill int64 - reverse bool - flush func(io.Writer, *space, [][]byte) -} - -type space struct { - space []byte - rwidth int - count int -} - -// NewBarFiller returns a BarFiller implementation which renders a -// progress bar in regular direction. If style is empty string, -// BarDefaultStyle is applied. To be used with `*Progress.Add(...) -// *Bar` method. -func NewBarFiller(style string) BarFiller { - return newBarFiller(style, false) -} - -// NewBarFillerRev returns a BarFiller implementation which renders a -// progress bar in reverse direction. If style is empty string, -// BarDefaultStyle is applied. To be used with `*Progress.Add(...) -// *Bar` method. -func NewBarFillerRev(style string) BarFiller { - return newBarFiller(style, true) -} - -// NewBarFillerPick pick between regular and reverse BarFiller implementation -// based on rev param. To be used with `*Progress.Add(...) *Bar` method. -func NewBarFillerPick(style string, rev bool) BarFiller { - return newBarFiller(style, rev) -} - -func newBarFiller(style string, rev bool) BarFiller { - bf := &barFiller{ - format: make([][]byte, len(BarDefaultStyle)), - rwidth: make([]int, len(BarDefaultStyle)), - reverse: rev, - } - bf.parse(BarDefaultStyle) - if style != "" && style != BarDefaultStyle { - bf.parse(style) - } - return bf -} - -func (s *barFiller) parse(style string) { - if !utf8.ValidString(style) { - panic("invalid bar style") - } - srcFormat := make([][]byte, len(BarDefaultStyle)) - srcRwidth := make([]int, len(BarDefaultStyle)) - i := 0 - for gr := uniseg.NewGraphemes(style); i < len(BarDefaultStyle) && gr.Next(); i++ { - srcFormat[i] = gr.Bytes() - srcRwidth[i] = runewidth.StringWidth(gr.Str()) - } - copy(s.format, srcFormat[:i]) - copy(s.rwidth, srcRwidth[:i]) - if s.reverse { - s.tip = s.format[rRevTip] - s.flush = reverseFlush - } else { - s.tip = s.format[rTip] - s.flush = regularFlush - } -} - -func (s *barFiller) Fill(w io.Writer, reqWidth int, stat decor.Statistics) { - width := internal.CheckRequestedWidth(reqWidth, stat.AvailableWidth) - brackets := s.rwidth[rLeft] + s.rwidth[rRight] - if width < brackets { - return - } - // don't count brackets as progress - width -= brackets - - w.Write(s.format[rLeft]) - defer w.Write(s.format[rRight]) - - cwidth := int(internal.PercentageRound(stat.Total, stat.Current, width)) - space := &space{ - space: s.format[rSpace], - rwidth: s.rwidth[rSpace], - count: width - cwidth, - } - - index, refill := 0, 0 - bb := make([][]byte, cwidth) - - if cwidth > 0 && cwidth != width { - bb[index] = s.tip - cwidth -= s.rwidth[rTip] - index++ - } - - if stat.Refill > 0 { - refill = int(internal.PercentageRound(stat.Total, int64(stat.Refill), width)) - if refill > cwidth { - refill = cwidth - } - cwidth -= refill - } - - for cwidth > 0 { - bb[index] = s.format[rFill] - cwidth -= s.rwidth[rFill] - index++ - } - - for refill > 0 { - bb[index] = s.format[rRefill] - refill -= s.rwidth[rRefill] - index++ - } - - if cwidth+refill < 0 || space.rwidth > 1 { - buf := new(bytes.Buffer) - s.flush(buf, space, bb[:index]) - io.WriteString(w, runewidth.Truncate(buf.String(), width, "…")) - return - } - - s.flush(w, space, bb) -} - -func regularFlush(w io.Writer, space *space, bb [][]byte) { - for i := len(bb) - 1; i >= 0; i-- { - w.Write(bb[i]) - } - for space.count > 0 { - w.Write(space.space) - space.count -= space.rwidth - } -} - -func reverseFlush(w io.Writer, space *space, bb [][]byte) { - for space.count > 0 { - w.Write(space.space) - space.count -= space.rwidth - } - for i := 0; i < len(bb); i++ { - w.Write(bb[i]) - } -} diff --git a/vendor/github.com/vbauerster/mpb/v6/bar_filler_spinner.go b/vendor/github.com/vbauerster/mpb/v6/bar_filler_spinner.go deleted file mode 100644 index 0817b19ec..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/bar_filler_spinner.go +++ /dev/null @@ -1,65 +0,0 @@ -package mpb - -import ( - "io" - "strings" - - "github.com/mattn/go-runewidth" - "github.com/vbauerster/mpb/v6/decor" - "github.com/vbauerster/mpb/v6/internal" -) - -// SpinnerAlignment enum. -type SpinnerAlignment int - -// SpinnerAlignment kinds. -const ( - SpinnerOnLeft SpinnerAlignment = iota - SpinnerOnMiddle - SpinnerOnRight -) - -// SpinnerDefaultStyle is a style for rendering a spinner. -var SpinnerDefaultStyle = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"} - -type spinnerFiller struct { - frames []string - count uint - alignment SpinnerAlignment -} - -// NewSpinnerFiller returns a BarFiller implementation which renders -// a spinner. If style is nil or zero length, SpinnerDefaultStyle is -// applied. To be used with `*Progress.Add(...) *Bar` method. -func NewSpinnerFiller(style []string, alignment SpinnerAlignment) BarFiller { - if len(style) == 0 { - style = SpinnerDefaultStyle - } - filler := &spinnerFiller{ - frames: style, - alignment: alignment, - } - return filler -} - -func (s *spinnerFiller) Fill(w io.Writer, reqWidth int, stat decor.Statistics) { - width := internal.CheckRequestedWidth(reqWidth, stat.AvailableWidth) - - frame := s.frames[s.count%uint(len(s.frames))] - frameWidth := runewidth.StringWidth(frame) - - if width < frameWidth { - return - } - - switch rest := width - frameWidth; s.alignment { - case SpinnerOnLeft: - io.WriteString(w, frame+strings.Repeat(" ", rest)) - case SpinnerOnMiddle: - str := strings.Repeat(" ", rest/2) + frame + strings.Repeat(" ", rest/2+rest%2) - io.WriteString(w, str) - case SpinnerOnRight: - io.WriteString(w, strings.Repeat(" ", rest)+frame) - } - s.count++ -} diff --git a/vendor/github.com/vbauerster/mpb/v6/bar_option.go b/vendor/github.com/vbauerster/mpb/v6/bar_option.go deleted file mode 100644 index e359c110c..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/bar_option.go +++ /dev/null @@ -1,153 +0,0 @@ -package mpb - -import ( - "bytes" - "io" - - "github.com/vbauerster/mpb/v6/decor" - "github.com/vbauerster/mpb/v6/internal" -) - -// BarOption is a func option to alter default behavior of a bar. -type BarOption func(*bState) - -func (s *bState) addDecorators(dest *[]decor.Decorator, decorators ...decor.Decorator) { - type mergeWrapper interface { - MergeUnwrap() []decor.Decorator - } - for _, decorator := range decorators { - if mw, ok := decorator.(mergeWrapper); ok { - *dest = append(*dest, mw.MergeUnwrap()...) - } - *dest = append(*dest, decorator) - } -} - -// AppendDecorators let you inject decorators to the bar's right side. -func AppendDecorators(decorators ...decor.Decorator) BarOption { - return func(s *bState) { - s.addDecorators(&s.aDecorators, decorators...) - } -} - -// PrependDecorators let you inject decorators to the bar's left side. -func PrependDecorators(decorators ...decor.Decorator) BarOption { - return func(s *bState) { - s.addDecorators(&s.pDecorators, decorators...) - } -} - -// BarID sets bar id. -func BarID(id int) BarOption { - return func(s *bState) { - s.id = id - } -} - -// BarWidth sets bar width independent of the container. -func BarWidth(width int) BarOption { - return func(s *bState) { - s.reqWidth = width - } -} - -// BarQueueAfter queues this (being constructed) bar to relplace -// runningBar after it has been completed. -func BarQueueAfter(runningBar *Bar) BarOption { - if runningBar == nil { - return nil - } - return func(s *bState) { - s.runningBar = runningBar - } -} - -// BarRemoveOnComplete removes both bar's filler and its decorators -// on complete event. -func BarRemoveOnComplete() BarOption { - return func(s *bState) { - s.dropOnComplete = true - } -} - -// BarFillerClearOnComplete clears bar's filler on complete event. -// It's shortcut for BarFillerOnComplete(""). -func BarFillerClearOnComplete() BarOption { - return BarFillerOnComplete("") -} - -// BarFillerOnComplete replaces bar's filler with message, on complete event. -func BarFillerOnComplete(message string) BarOption { - return BarFillerMiddleware(func(base BarFiller) BarFiller { - return BarFillerFunc(func(w io.Writer, reqWidth int, st decor.Statistics) { - if st.Completed { - io.WriteString(w, message) - } else { - base.Fill(w, reqWidth, st) - } - }) - }) -} - -// BarFillerMiddleware provides a way to augment the underlying BarFiller. -func BarFillerMiddleware(middle func(BarFiller) BarFiller) BarOption { - return func(s *bState) { - s.middleware = middle - } -} - -// BarPriority sets bar's priority. Zero is highest priority, i.e. bar -// will be on top. If `BarReplaceOnComplete` option is supplied, this -// option is ignored. -func BarPriority(priority int) BarOption { - return func(s *bState) { - s.priority = priority - } -} - -// BarExtender provides a way to extend bar to the next new line. -func BarExtender(filler BarFiller) BarOption { - if filler == nil { - return nil - } - return func(s *bState) { - s.extender = makeExtenderFunc(filler) - } -} - -func makeExtenderFunc(filler BarFiller) extenderFunc { - buf := new(bytes.Buffer) - return func(r io.Reader, reqWidth int, st decor.Statistics) (io.Reader, int) { - filler.Fill(buf, reqWidth, st) - return io.MultiReader(r, buf), bytes.Count(buf.Bytes(), []byte("\n")) - } -} - -// BarFillerTrim removes leading and trailing space around the underlying BarFiller. -func BarFillerTrim() BarOption { - return func(s *bState) { - s.trimSpace = true - } -} - -// BarNoPop disables bar pop out of container. Effective when -// PopCompletedMode of container is enabled. -func BarNoPop() BarOption { - return func(s *bState) { - s.noPop = true - } -} - -// BarOptional will invoke provided option only when pick is true. -func BarOptional(option BarOption, pick bool) BarOption { - return BarOptOn(option, internal.Predicate(pick)) -} - -// BarOptOn will invoke provided option only when higher order predicate -// evaluates to true. -func BarOptOn(option BarOption, predicate func() bool) BarOption { - if predicate() { - return option - } - return nil -} diff --git a/vendor/github.com/vbauerster/mpb/v6/container_option.go b/vendor/github.com/vbauerster/mpb/v6/container_option.go deleted file mode 100644 index b92c7578c..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/container_option.go +++ /dev/null @@ -1,112 +0,0 @@ -package mpb - -import ( - "io" - "io/ioutil" - "sync" - "time" - - "github.com/vbauerster/mpb/v6/internal" -) - -// ContainerOption is a func option to alter default behavior of a bar -// container. Container term refers to a Progress struct which can -// hold one or more Bars. -type ContainerOption func(*pState) - -// WithWaitGroup provides means to have a single joint point. If -// *sync.WaitGroup is provided, you can safely call just p.Wait() -// without calling Wait() on provided *sync.WaitGroup. Makes sense -// when there are more than one bar to render. -func WithWaitGroup(wg *sync.WaitGroup) ContainerOption { - return func(s *pState) { - s.uwg = wg - } -} - -// WithWidth sets container width. If not set it defaults to terminal -// width. A bar added to the container will inherit its width, unless -// overridden by `func BarWidth(int) BarOption`. -func WithWidth(width int) ContainerOption { - return func(s *pState) { - s.reqWidth = width - } -} - -// WithRefreshRate overrides default 120ms refresh rate. -func WithRefreshRate(d time.Duration) ContainerOption { - return func(s *pState) { - s.rr = d - } -} - -// WithManualRefresh disables internal auto refresh time.Ticker. -// Refresh will occur upon receive value from provided ch. -func WithManualRefresh(ch <-chan interface{}) ContainerOption { - return func(s *pState) { - s.externalRefresh = ch - } -} - -// WithRenderDelay delays rendering. By default rendering starts as -// soon as bar is added, with this option it's possible to delay -// rendering process by keeping provided chan unclosed. In other words -// rendering will start as soon as provided chan is closed. -func WithRenderDelay(ch <-chan struct{}) ContainerOption { - return func(s *pState) { - s.renderDelay = ch - } -} - -// WithShutdownNotifier provided chanel will be closed, after all bars -// have been rendered. -func WithShutdownNotifier(ch chan struct{}) ContainerOption { - return func(s *pState) { - s.shutdownNotifier = ch - } -} - -// WithOutput overrides default os.Stdout output. Setting it to nil -// will effectively disable auto refresh rate and discard any output, -// useful if you want to disable progress bars with little overhead. -func WithOutput(w io.Writer) ContainerOption { - return func(s *pState) { - if w == nil { - s.output = ioutil.Discard - s.outputDiscarded = true - return - } - s.output = w - } -} - -// WithDebugOutput sets debug output. -func WithDebugOutput(w io.Writer) ContainerOption { - if w == nil { - return nil - } - return func(s *pState) { - s.debugOut = w - } -} - -// PopCompletedMode will pop and stop rendering completed bars. -func PopCompletedMode() ContainerOption { - return func(s *pState) { - s.popCompleted = true - } -} - -// ContainerOptional will invoke provided option only when pick is true. -func ContainerOptional(option ContainerOption, pick bool) ContainerOption { - return ContainerOptOn(option, internal.Predicate(pick)) -} - -// ContainerOptOn will invoke provided option only when higher order -// predicate evaluates to true. -func ContainerOptOn(option ContainerOption, predicate func() bool) ContainerOption { - if predicate() { - return option - } - return nil -} diff --git a/vendor/github.com/vbauerster/mpb/v6/cwriter/doc.go b/vendor/github.com/vbauerster/mpb/v6/cwriter/doc.go deleted file mode 100644 index 93c8f8268..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/cwriter/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package cwriter is a console writer abstraction for the underlying OS. -package cwriter diff --git a/vendor/github.com/vbauerster/mpb/v6/cwriter/util_bsd.go b/vendor/github.com/vbauerster/mpb/v6/cwriter/util_bsd.go deleted file mode 100644 index 4e3564ece..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/cwriter/util_bsd.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build darwin dragonfly freebsd netbsd openbsd - -package cwriter - -import "golang.org/x/sys/unix" - -const ioctlReadTermios = unix.TIOCGETA diff --git a/vendor/github.com/vbauerster/mpb/v6/cwriter/util_linux.go b/vendor/github.com/vbauerster/mpb/v6/cwriter/util_linux.go deleted file mode 100644 index 253f12dd2..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/cwriter/util_linux.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build aix linux - -package cwriter - -import "golang.org/x/sys/unix" - -const ioctlReadTermios = unix.TCGETS diff --git a/vendor/github.com/vbauerster/mpb/v6/cwriter/util_solaris.go b/vendor/github.com/vbauerster/mpb/v6/cwriter/util_solaris.go deleted file mode 100644 index 4b29ff5c0..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/cwriter/util_solaris.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build solaris - -package cwriter - -import "golang.org/x/sys/unix" - -const ioctlReadTermios = unix.TCGETA diff --git a/vendor/github.com/vbauerster/mpb/v6/cwriter/writer.go b/vendor/github.com/vbauerster/mpb/v6/cwriter/writer.go deleted file mode 100644 index 1ade54761..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/cwriter/writer.go +++ /dev/null @@ -1,84 +0,0 @@ -package cwriter - -import ( - "bytes" - "errors" - "io" - "os" - "strconv" -) - -// ErrNotTTY not a TeleTYpewriter error. -var ErrNotTTY = errors.New("not a terminal") - -// http://ascii-table.com/ansi-escape-sequences.php -const ( - escOpen = "\x1b[" - cuuAndEd = "A\x1b[J" -) - -// Writer is a buffered the writer that updates the terminal. The -// contents of writer will be flushed when Flush is called. -type Writer struct { - out io.Writer - buf bytes.Buffer - lineCount int - fd int - isTerminal bool -} - -// New returns a new Writer with defaults. -func New(out io.Writer) *Writer { - w := &Writer{out: out} - if f, ok := out.(*os.File); ok { - w.fd = int(f.Fd()) - w.isTerminal = IsTerminal(w.fd) - } - return w -} - -// Flush flushes the underlying buffer. -func (w *Writer) Flush(lineCount int) (err error) { - // some terminals interpret 'cursor up 0' as 'cursor up 1' - if w.lineCount > 0 { - err = w.clearLines() - if err != nil { - return - } - } - w.lineCount = lineCount - _, err = w.buf.WriteTo(w.out) - return -} - -// Write appends the contents of p to the underlying buffer. -func (w *Writer) Write(p []byte) (n int, err error) { - return w.buf.Write(p) -} - -// WriteString writes string to the underlying buffer. -func (w *Writer) WriteString(s string) (n int, err error) { - return w.buf.WriteString(s) -} - -// ReadFrom reads from the provided io.Reader and writes to the -// underlying buffer. -func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) { - return w.buf.ReadFrom(r) -} - -// GetWidth returns width of underlying terminal. -func (w *Writer) GetWidth() (int, error) { - if !w.isTerminal { - return -1, ErrNotTTY - } - tw, _, err := GetSize(w.fd) - return tw, err -} - -func (w *Writer) ansiCuuAndEd() (err error) { - buf := make([]byte, 8) - buf = strconv.AppendInt(buf[:copy(buf, escOpen)], int64(w.lineCount), 10) - _, err = w.out.Write(append(buf, cuuAndEd...)) - return -} diff --git a/vendor/github.com/vbauerster/mpb/v6/cwriter/writer_posix.go b/vendor/github.com/vbauerster/mpb/v6/cwriter/writer_posix.go deleted file mode 100644 index f54a5d06b..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/cwriter/writer_posix.go +++ /dev/null @@ -1,26 +0,0 @@ -// +build !windows - -package cwriter - -import ( - "golang.org/x/sys/unix" -) - -func (w *Writer) clearLines() error { - return w.ansiCuuAndEd() -} - -// GetSize returns the dimensions of the given terminal. -func GetSize(fd int) (width, height int, err error) { - ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ) - if err != nil { - return -1, -1, err - } - return int(ws.Col), int(ws.Row), nil -} - -// IsTerminal returns whether the given file descriptor is a terminal. -func IsTerminal(fd int) bool { - _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) - return err == nil -} diff --git a/vendor/github.com/vbauerster/mpb/v6/cwriter/writer_windows.go b/vendor/github.com/vbauerster/mpb/v6/cwriter/writer_windows.go deleted file mode 100644 index 1a69c81ac..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/cwriter/writer_windows.go +++ /dev/null @@ -1,73 +0,0 @@ -// +build windows - -package cwriter - -import ( - "unsafe" - - "golang.org/x/sys/windows" -) - -var kernel32 = windows.NewLazySystemDLL("kernel32.dll") - -var ( - procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") - procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") -) - -func (w *Writer) clearLines() error { - if !w.isTerminal { - // hope it's cygwin or similar - return w.ansiCuuAndEd() - } - - var info windows.ConsoleScreenBufferInfo - if err := windows.GetConsoleScreenBufferInfo(windows.Handle(w.fd), &info); err != nil { - return err - } - - info.CursorPosition.Y -= int16(w.lineCount) - if info.CursorPosition.Y < 0 { - info.CursorPosition.Y = 0 - } - _, _, _ = procSetConsoleCursorPosition.Call( - uintptr(w.fd), - uintptr(uint32(uint16(info.CursorPosition.Y))<<16|uint32(uint16(info.CursorPosition.X))), - ) - - // clear the lines - cursor := &windows.Coord{ - X: info.Window.Left, - Y: info.CursorPosition.Y, - } - count := uint32(info.Size.X) * uint32(w.lineCount) - _, _, _ = procFillConsoleOutputCharacter.Call( - uintptr(w.fd), - uintptr(' '), - uintptr(count), - *(*uintptr)(unsafe.Pointer(cursor)), - uintptr(unsafe.Pointer(new(uint32))), - ) - return nil -} - -// GetSize returns the visible dimensions of the given terminal. -// -// These dimensions don't include any scrollback buffer height. -func GetSize(fd int) (width, height int, err error) { - var info windows.ConsoleScreenBufferInfo - if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil { - return 0, 0, err - } - // terminal.GetSize from crypto/ssh adds "+ 1" to both width and height: - // https://go.googlesource.com/crypto/+/refs/heads/release-branch.go1.14/ssh/terminal/util_windows.go#75 - // but looks like this is a root cause of issue #66, so removing both "+ 1" have fixed it. - return int(info.Window.Right - info.Window.Left), int(info.Window.Bottom - info.Window.Top), nil -} - -// IsTerminal returns whether the given file descriptor is a terminal. -func IsTerminal(fd int) bool { - var st uint32 - err := windows.GetConsoleMode(windows.Handle(fd), &st) - return err == nil -} diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/any.go b/vendor/github.com/vbauerster/mpb/v6/decor/any.go deleted file mode 100644 index 39518f594..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/decor/any.go +++ /dev/null @@ -1,21 +0,0 @@ -package decor - -// Any decorator displays text, that can be changed during decorator's -// lifetime via provided DecorFunc. -// -// `fn` DecorFunc callback -// -// `wcc` optional WC config -// -func Any(fn DecorFunc, wcc ...WC) Decorator { - return &any{initWC(wcc...), fn} -} - -type any struct { - WC - fn DecorFunc -} - -func (d *any) Decor(s Statistics) string { - return d.FormatMsg(d.fn(s)) -} diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/counters.go b/vendor/github.com/vbauerster/mpb/v6/decor/counters.go deleted file mode 100644 index 4a5343d41..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/decor/counters.go +++ /dev/null @@ -1,243 +0,0 @@ -package decor - -import ( - "fmt" - "strings" -) - -const ( - _ = iota - UnitKiB - UnitKB -) - -// CountersNoUnit is a wrapper around Counters with no unit param. -func CountersNoUnit(pairFmt string, wcc ...WC) Decorator { - return Counters(0, pairFmt, wcc...) -} - -// CountersKibiByte is a wrapper around Counters with predefined unit -// UnitKiB (bytes/1024). -func CountersKibiByte(pairFmt string, wcc ...WC) Decorator { - return Counters(UnitKiB, pairFmt, wcc...) -} - -// CountersKiloByte is a wrapper around Counters with predefined unit -// UnitKB (bytes/1000). -func CountersKiloByte(pairFmt string, wcc ...WC) Decorator { - return Counters(UnitKB, pairFmt, wcc...) -} - -// Counters decorator with dynamic unit measure adjustment. -// -// `unit` one of [0|UnitKiB|UnitKB] zero for no unit -// -// `pairFmt` printf compatible verbs for current and total pair -// -// `wcc` optional WC config -// -// pairFmt example if unit=UnitKB: -// -// pairFmt="%.1f / %.1f" output: "1.0MB / 12.0MB" -// pairFmt="% .1f / % .1f" output: "1.0 MB / 12.0 MB" -// pairFmt="%d / %d" output: "1MB / 12MB" -// pairFmt="% d / % d" output: "1 MB / 12 MB" -// -func Counters(unit int, pairFmt string, wcc ...WC) Decorator { - producer := func(unit int, pairFmt string) DecorFunc { - if pairFmt == "" { - pairFmt = "%d / %d" - } else if strings.Count(pairFmt, "%") != 2 { - panic("expected pairFmt with exactly 2 verbs") - } - switch unit { - case UnitKiB: - return func(s Statistics) string { - return fmt.Sprintf(pairFmt, SizeB1024(s.Current), SizeB1024(s.Total)) - } - case UnitKB: - return func(s Statistics) string { - return fmt.Sprintf(pairFmt, SizeB1000(s.Current), SizeB1000(s.Total)) - } - default: - return func(s Statistics) string { - return fmt.Sprintf(pairFmt, s.Current, s.Total) - } - } - } - return Any(producer(unit, pairFmt), wcc...) -} - -// TotalNoUnit is a wrapper around Total with no unit param. -func TotalNoUnit(format string, wcc ...WC) Decorator { - return Total(0, format, wcc...) -} - -// TotalKibiByte is a wrapper around Total with predefined unit -// UnitKiB (bytes/1024). -func TotalKibiByte(format string, wcc ...WC) Decorator { - return Total(UnitKiB, format, wcc...) -} - -// TotalKiloByte is a wrapper around Total with predefined unit -// UnitKB (bytes/1000). -func TotalKiloByte(format string, wcc ...WC) Decorator { - return Total(UnitKB, format, wcc...) -} - -// Total decorator with dynamic unit measure adjustment. -// -// `unit` one of [0|UnitKiB|UnitKB] zero for no unit -// -// `format` printf compatible verb for Total -// -// `wcc` optional WC config -// -// format example if unit=UnitKiB: -// -// format="%.1f" output: "12.0MiB" -// format="% .1f" output: "12.0 MiB" -// format="%d" output: "12MiB" -// format="% d" output: "12 MiB" -// -func Total(unit int, format string, wcc ...WC) Decorator { - producer := func(unit int, format string) DecorFunc { - if format == "" { - format = "%d" - } else if strings.Count(format, "%") != 1 { - panic("expected format with exactly 1 verb") - } - - switch unit { - case UnitKiB: - return func(s Statistics) string { - return fmt.Sprintf(format, SizeB1024(s.Total)) - } - case UnitKB: - return func(s Statistics) string { - return fmt.Sprintf(format, SizeB1000(s.Total)) - } - default: - return func(s Statistics) string { - return fmt.Sprintf(format, s.Total) - } - } - } - return Any(producer(unit, format), wcc...) -} - -// CurrentNoUnit is a wrapper around Current with no unit param. -func CurrentNoUnit(format string, wcc ...WC) Decorator { - return Current(0, format, wcc...) -} - -// CurrentKibiByte is a wrapper around Current with predefined unit -// UnitKiB (bytes/1024). -func CurrentKibiByte(format string, wcc ...WC) Decorator { - return Current(UnitKiB, format, wcc...) -} - -// CurrentKiloByte is a wrapper around Current with predefined unit -// UnitKB (bytes/1000). -func CurrentKiloByte(format string, wcc ...WC) Decorator { - return Current(UnitKB, format, wcc...) -} - -// Current decorator with dynamic unit measure adjustment. -// -// `unit` one of [0|UnitKiB|UnitKB] zero for no unit -// -// `format` printf compatible verb for Current -// -// `wcc` optional WC config -// -// format example if unit=UnitKiB: -// -// format="%.1f" output: "12.0MiB" -// format="% .1f" output: "12.0 MiB" -// format="%d" output: "12MiB" -// format="% d" output: "12 MiB" -// -func Current(unit int, format string, wcc ...WC) Decorator { - producer := func(unit int, format string) DecorFunc { - if format == "" { - format = "%d" - } else if strings.Count(format, "%") != 1 { - panic("expected format with exactly 1 verb") - } - - switch unit { - case UnitKiB: - return func(s Statistics) string { - return fmt.Sprintf(format, SizeB1024(s.Current)) - } - case UnitKB: - return func(s Statistics) string { - return fmt.Sprintf(format, SizeB1000(s.Current)) - } - default: - return func(s Statistics) string { - return fmt.Sprintf(format, s.Current) - } - } - } - return Any(producer(unit, format), wcc...) -} - -// InvertedCurrentNoUnit is a wrapper around InvertedCurrent with no unit param. -func InvertedCurrentNoUnit(format string, wcc ...WC) Decorator { - return InvertedCurrent(0, format, wcc...) -} - -// InvertedCurrentKibiByte is a wrapper around InvertedCurrent with predefined unit -// UnitKiB (bytes/1024). -func InvertedCurrentKibiByte(format string, wcc ...WC) Decorator { - return InvertedCurrent(UnitKiB, format, wcc...) -} - -// InvertedCurrentKiloByte is a wrapper around InvertedCurrent with predefined unit -// UnitKB (bytes/1000). -func InvertedCurrentKiloByte(format string, wcc ...WC) Decorator { - return InvertedCurrent(UnitKB, format, wcc...) -} - -// InvertedCurrent decorator with dynamic unit measure adjustment. -// -// `unit` one of [0|UnitKiB|UnitKB] zero for no unit -// -// `format` printf compatible verb for InvertedCurrent -// -// `wcc` optional WC config -// -// format example if unit=UnitKiB: -// -// format="%.1f" output: "12.0MiB" -// format="% .1f" output: "12.0 MiB" -// format="%d" output: "12MiB" -// format="% d" output: "12 MiB" -// -func InvertedCurrent(unit int, format string, wcc ...WC) Decorator { - producer := func(unit int, format string) DecorFunc { - if format == "" { - format = "%d" - } else if strings.Count(format, "%") != 1 { - panic("expected format with exactly 1 verb") - } - - switch unit { - case UnitKiB: - return func(s Statistics) string { - return fmt.Sprintf(format, SizeB1024(s.Total-s.Current)) - } - case UnitKB: - return func(s Statistics) string { - return fmt.Sprintf(format, SizeB1000(s.Total-s.Current)) - } - default: - return func(s Statistics) string { - return fmt.Sprintf(format, s.Total-s.Current) - } - } - } - return Any(producer(unit, format), wcc...) -} diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/decorator.go b/vendor/github.com/vbauerster/mpb/v6/decor/decorator.go deleted file mode 100644 index e81fae367..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/decor/decorator.go +++ /dev/null @@ -1,191 +0,0 @@ -package decor - -import ( - "fmt" - "time" - - "github.com/acarl005/stripansi" - "github.com/mattn/go-runewidth" -) - -const ( - // DidentRight bit specifies identation direction. - // |foo |b | With DidentRight - // | foo| b| Without DidentRight - DidentRight = 1 << iota - - // DextraSpace bit adds extra space, makes sense with DSyncWidth only. - // When DidentRight bit set, the space will be added to the right, - // otherwise to the left. - DextraSpace - - // DSyncWidth bit enables same column width synchronization. - // Effective with multiple bars only. - DSyncWidth - - // DSyncWidthR is shortcut for DSyncWidth|DidentRight - DSyncWidthR = DSyncWidth | DidentRight - - // DSyncSpace is shortcut for DSyncWidth|DextraSpace - DSyncSpace = DSyncWidth | DextraSpace - - // DSyncSpaceR is shortcut for DSyncWidth|DextraSpace|DidentRight - DSyncSpaceR = DSyncWidth | DextraSpace | DidentRight -) - -// TimeStyle enum. -type TimeStyle int - -// TimeStyle kinds. -const ( - ET_STYLE_GO TimeStyle = iota - ET_STYLE_HHMMSS - ET_STYLE_HHMM - ET_STYLE_MMSS -) - -// Statistics consists of progress related statistics, that Decorator -// may need. -type Statistics struct { - ID int - AvailableWidth int - Total int64 - Current int64 - Refill int64 - Completed bool -} - -// Decorator interface. -// Most of the time there is no need to implement this interface -// manually, as decor package already provides a wide range of decorators -// which implement this interface. If however built-in decorators don't -// meet your needs, you're free to implement your own one by implementing -// this particular interface. The easy way to go is to convert a -// `DecorFunc` into a `Decorator` interface by using provided -// `func Any(DecorFunc, ...WC) Decorator`. -type Decorator interface { - Configurator - Synchronizer - Decor(Statistics) string -} - -// DecorFunc func type. -// To be used with `func Any`(DecorFunc, ...WC) Decorator`. -type DecorFunc func(Statistics) string - -// Synchronizer interface. -// All decorators implement this interface implicitly. Its Sync -// method exposes width sync channel, if DSyncWidth bit is set. -type Synchronizer interface { - Sync() (chan int, bool) -} - -// Configurator interface. -type Configurator interface { - GetConf() WC - SetConf(WC) -} - -// Wrapper interface. -// If you're implementing custom Decorator by wrapping a built-in one, -// it is necessary to implement this interface to retain functionality -// of built-in Decorator. -type Wrapper interface { - Base() Decorator -} - -// EwmaDecorator interface. -// EWMA based decorators should implement this one. -type EwmaDecorator interface { - EwmaUpdate(int64, time.Duration) -} - -// AverageDecorator interface. -// Average decorators should implement this interface to provide start -// time adjustment facility, for resume-able tasks. -type AverageDecorator interface { - AverageAdjust(time.Time) -} - -// ShutdownListener interface. -// If decorator needs to be notified once upon bar shutdown event, so -// this is the right interface to implement. -type ShutdownListener interface { - Shutdown() -} - -// Global convenience instances of WC with sync width bit set. -// To be used with multiple bars only, i.e. not effective for single bar usage. -var ( - WCSyncWidth = WC{C: DSyncWidth} - WCSyncWidthR = WC{C: DSyncWidthR} - WCSyncSpace = WC{C: DSyncSpace} - WCSyncSpaceR = WC{C: DSyncSpaceR} -) - -// WC is a struct with two public fields W and C, both of int type. -// W represents width and C represents bit set of width related config. -// A decorator should embed WC, to enable width synchronization. -type WC struct { - W int - C int - fill func(s string, w int) string - wsync chan int -} - -// FormatMsg formats final message according to WC.W and WC.C. -// Should be called by any Decorator implementation. -func (wc *WC) FormatMsg(msg string) string { - pureWidth := runewidth.StringWidth(msg) - stripWidth := runewidth.StringWidth(stripansi.Strip(msg)) - maxCell := wc.W - if (wc.C & DSyncWidth) != 0 { - cellCount := stripWidth - if (wc.C & DextraSpace) != 0 { - cellCount++ - } - wc.wsync <- cellCount - maxCell = <-wc.wsync - } - return wc.fill(msg, maxCell+(pureWidth-stripWidth)) -} - -// Init initializes width related config. -func (wc *WC) Init() WC { - wc.fill = runewidth.FillLeft - if (wc.C & DidentRight) != 0 { - wc.fill = runewidth.FillRight - } - if (wc.C & DSyncWidth) != 0 { - // it's deliberate choice to override wsync on each Init() call, - // this way globals like WCSyncSpace can be reused - wc.wsync = make(chan int) - } - return *wc -} - -// Sync is implementation of Synchronizer interface. -func (wc *WC) Sync() (chan int, bool) { - if (wc.C&DSyncWidth) != 0 && wc.wsync == nil { - panic(fmt.Sprintf("%T is not initialized", wc)) - } - return wc.wsync, (wc.C & DSyncWidth) != 0 -} - -// GetConf is implementation of Configurator interface. -func (wc *WC) GetConf() WC { - return *wc -} - -// SetConf is implementation of Configurator interface. -func (wc *WC) SetConf(conf WC) { - *wc = conf.Init() -} - -func initWC(wcc ...WC) WC { - var wc WC - for _, nwc := range wcc { - wc = nwc - } - return wc.Init() -} diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/doc.go b/vendor/github.com/vbauerster/mpb/v6/decor/doc.go deleted file mode 100644 index bfbb82e11..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/decor/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -// Package decor provides common decorators for "github.com/vbauerster/mpb/v6" module. -/* - Some decorators returned by this package might have a closure state. It is ok to use - decorators concurrently, unless you share the same decorator among multiple - *mpb.Bar instances. To avoid data races, create new decorator per *mpb.Bar instance. - - Don't: - - p := mpb.New() - name := decor.Name("bar") - p.AddBar(100, mpb.AppendDecorators(name)) - p.AddBar(100, mpb.AppendDecorators(name)) - - Do: - - p := mpb.New() - p.AddBar(100, mpb.AppendDecorators(decor.Name("bar1"))) - p.AddBar(100, mpb.AppendDecorators(decor.Name("bar2"))) -*/ -package decor diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/elapsed.go b/vendor/github.com/vbauerster/mpb/v6/decor/elapsed.go deleted file mode 100644 index e389f1581..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/decor/elapsed.go +++ /dev/null @@ -1,35 +0,0 @@ -package decor - -import ( - "time" -) - -// Elapsed decorator. It's wrapper of NewElapsed. -// -// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS] -// -// `wcc` optional WC config -// -func Elapsed(style TimeStyle, wcc ...WC) Decorator { - return NewElapsed(style, time.Now(), wcc...) -} - -// NewElapsed returns elapsed time decorator. -// -// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS] -// -// `startTime` start time -// -// `wcc` optional WC config -// -func NewElapsed(style TimeStyle, startTime time.Time, wcc ...WC) Decorator { - var msg string - producer := chooseTimeProducer(style) - fn := func(s Statistics) string { - if !s.Completed { - msg = producer(time.Since(startTime)) - } - return msg - } - return Any(fn, wcc...) -} diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/eta.go b/vendor/github.com/vbauerster/mpb/v6/decor/eta.go deleted file mode 100644 index d03caa735..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/decor/eta.go +++ /dev/null @@ -1,203 +0,0 @@ -package decor - -import ( - "fmt" - "math" - "time" - - "github.com/VividCortex/ewma" -) - -// TimeNormalizer interface. Implementors could be passed into -// MovingAverageETA, in order to affect i.e. normalize its output. -type TimeNormalizer interface { - Normalize(time.Duration) time.Duration -} - -// TimeNormalizerFunc is function type adapter to convert function -// into TimeNormalizer. -type TimeNormalizerFunc func(time.Duration) time.Duration - -func (f TimeNormalizerFunc) Normalize(src time.Duration) time.Duration { - return f(src) -} - -// EwmaETA exponential-weighted-moving-average based ETA decorator. -// For this decorator to work correctly you have to measure each -// iteration's duration and pass it to the -// *Bar.DecoratorEwmaUpdate(time.Duration) method after each increment. -func EwmaETA(style TimeStyle, age float64, wcc ...WC) Decorator { - var average ewma.MovingAverage - if age == 0 { - average = ewma.NewMovingAverage() - } else { - average = ewma.NewMovingAverage(age) - } - return MovingAverageETA(style, NewThreadSafeMovingAverage(average), nil, wcc...) -} - -// MovingAverageETA decorator relies on MovingAverage implementation to calculate its average. -// -// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS] -// -// `average` implementation of MovingAverage interface -// -// `normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer] -// -// `wcc` optional WC config -// -func MovingAverageETA(style TimeStyle, average ewma.MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator { - d := &movingAverageETA{ - WC: initWC(wcc...), - average: average, - normalizer: normalizer, - producer: chooseTimeProducer(style), - } - return d -} - -type movingAverageETA struct { - WC - average ewma.MovingAverage - normalizer TimeNormalizer - producer func(time.Duration) string -} - -func (d *movingAverageETA) Decor(s Statistics) string { - v := math.Round(d.average.Value()) - remaining := time.Duration((s.Total - s.Current) * int64(v)) - if d.normalizer != nil { - remaining = d.normalizer.Normalize(remaining) - } - return d.FormatMsg(d.producer(remaining)) -} - -func (d *movingAverageETA) EwmaUpdate(n int64, dur time.Duration) { - durPerItem := float64(dur) / float64(n) - if math.IsInf(durPerItem, 0) || math.IsNaN(durPerItem) { - return - } - d.average.Add(durPerItem) -} - -// AverageETA decorator. It's wrapper of NewAverageETA. -// -// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS] -// -// `wcc` optional WC config -// -func AverageETA(style TimeStyle, wcc ...WC) Decorator { - return NewAverageETA(style, time.Now(), nil, wcc...) -} - -// NewAverageETA decorator with user provided start time. -// -// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS] -// -// `startTime` start time -// -// `normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer] -// -// `wcc` optional WC config -// -func NewAverageETA(style TimeStyle, startTime time.Time, normalizer TimeNormalizer, wcc ...WC) Decorator { - d := &averageETA{ - WC: initWC(wcc...), - startTime: startTime, - normalizer: normalizer, - producer: chooseTimeProducer(style), - } - return d -} - -type averageETA struct { - WC - startTime time.Time - normalizer TimeNormalizer - producer func(time.Duration) string -} - -func (d *averageETA) Decor(s Statistics) string { - var remaining time.Duration - if s.Current != 0 { - durPerItem := float64(time.Since(d.startTime)) / float64(s.Current) - durPerItem = math.Round(durPerItem) - remaining = time.Duration((s.Total - s.Current) * int64(durPerItem)) - if d.normalizer != nil { - remaining = d.normalizer.Normalize(remaining) - } - } - return d.FormatMsg(d.producer(remaining)) -} - -func (d *averageETA) AverageAdjust(startTime time.Time) { - d.startTime = startTime -} - -// MaxTolerateTimeNormalizer returns implementation of TimeNormalizer. -func MaxTolerateTimeNormalizer(maxTolerate time.Duration) TimeNormalizer { - var normalized time.Duration - var lastCall time.Time - return TimeNormalizerFunc(func(remaining time.Duration) time.Duration { - if diff := normalized - remaining; diff <= 0 || diff > maxTolerate || remaining < time.Minute { - normalized = remaining - lastCall = time.Now() - return remaining - } - normalized -= time.Since(lastCall) - lastCall = time.Now() - return normalized - }) -} - -// FixedIntervalTimeNormalizer returns implementation of TimeNormalizer. -func FixedIntervalTimeNormalizer(updInterval int) TimeNormalizer { - var normalized time.Duration - var lastCall time.Time - var count int - return TimeNormalizerFunc(func(remaining time.Duration) time.Duration { - if count == 0 || remaining < time.Minute { - count = updInterval - normalized = remaining - lastCall = time.Now() - return remaining - } - count-- - normalized -= time.Since(lastCall) - lastCall = time.Now() - return normalized - }) -} - -func chooseTimeProducer(style TimeStyle) func(time.Duration) string { - switch style { - case ET_STYLE_HHMMSS: - return func(remaining time.Duration) string { - hours := int64(remaining/time.Hour) % 60 - minutes := int64(remaining/time.Minute) % 60 - seconds := int64(remaining/time.Second) % 60 - return fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds) - } - case ET_STYLE_HHMM: - return func(remaining time.Duration) string { - hours := int64(remaining/time.Hour) % 60 - minutes := int64(remaining/time.Minute) % 60 - return fmt.Sprintf("%02d:%02d", hours, minutes) - } - case ET_STYLE_MMSS: - return func(remaining time.Duration) string { - hours := int64(remaining/time.Hour) % 60 - minutes := int64(remaining/time.Minute) % 60 - seconds := int64(remaining/time.Second) % 60 - if hours > 0 { - return fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds) - } - return fmt.Sprintf("%02d:%02d", minutes, seconds) - } - default: - return func(remaining time.Duration) string { - // strip off nanoseconds - return ((remaining / time.Second) * time.Second).String() - } - } -} diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/merge.go b/vendor/github.com/vbauerster/mpb/v6/decor/merge.go deleted file mode 100644 index e41406a64..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/decor/merge.go +++ /dev/null @@ -1,107 +0,0 @@ -package decor - -import ( - "strings" - - "github.com/acarl005/stripansi" - "github.com/mattn/go-runewidth" -) - -// Merge wraps its decorator argument with intention to sync width -// with several decorators of another bar. Visual example: -// -// +----+--------+---------+--------+ -// | B1 | MERGE(D, P1, Pn) | -// +----+--------+---------+--------+ -// | B2 | D0 | D1 | Dn | -// +----+--------+---------+--------+ -// -func Merge(decorator Decorator, placeholders ...WC) Decorator { - if _, ok := decorator.Sync(); !ok || len(placeholders) == 0 { - return decorator - } - md := &mergeDecorator{ - Decorator: decorator, - wc: decorator.GetConf(), - placeHolders: make([]*placeHolderDecorator, len(placeholders)), - } - decorator.SetConf(WC{}) - for i, wc := range placeholders { - if (wc.C & DSyncWidth) == 0 { - return decorator - } - md.placeHolders[i] = &placeHolderDecorator{wc.Init()} - } - return md -} - -type mergeDecorator struct { - Decorator - wc WC - placeHolders []*placeHolderDecorator -} - -func (d *mergeDecorator) GetConf() WC { - return d.wc -} - -func (d *mergeDecorator) SetConf(conf WC) { - d.wc = conf.Init() -} - -func (d *mergeDecorator) MergeUnwrap() []Decorator { - decorators := make([]Decorator, len(d.placeHolders)) - for i, ph := range d.placeHolders { - decorators[i] = ph - } - return decorators -} - -func (d *mergeDecorator) Sync() (chan int, bool) { - return d.wc.Sync() -} - -func (d *mergeDecorator) Base() Decorator { - return d.Decorator -} - -func (d *mergeDecorator) Decor(s Statistics) string { - msg := d.Decorator.Decor(s) - pureWidth := runewidth.StringWidth(msg) - stripWidth := runewidth.StringWidth(stripansi.Strip(msg)) - cellCount := stripWidth - if (d.wc.C & DextraSpace) != 0 { - cellCount++ - } - - total := runewidth.StringWidth(d.placeHolders[0].FormatMsg("")) - pw := (cellCount - total) / len(d.placeHolders) - rem := (cellCount - total) % len(d.placeHolders) - - var diff int - for i := 1; i < len(d.placeHolders); i++ { - ph := d.placeHolders[i] - width := pw - diff - if (ph.WC.C & DextraSpace) != 0 { - width-- - if width < 0 { - width = 0 - } - } - max := runewidth.StringWidth(ph.FormatMsg(strings.Repeat(" ", width))) - total += max - diff = max - pw - } - - d.wc.wsync <- pw + rem - max := <-d.wc.wsync - return d.wc.fill(msg, max+total+(pureWidth-stripWidth)) -} - -type placeHolderDecorator struct { - WC -} - -func (d *placeHolderDecorator) Decor(Statistics) string { - return "" -} diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/moving_average.go b/vendor/github.com/vbauerster/mpb/v6/decor/moving_average.go deleted file mode 100644 index 50ac9c393..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/decor/moving_average.go +++ /dev/null @@ -1,68 +0,0 @@ -package decor - -import ( - "sort" - "sync" - - "github.com/VividCortex/ewma" -) - -type threadSafeMovingAverage struct { - ewma.MovingAverage - mu sync.Mutex -} - -func (s *threadSafeMovingAverage) Add(value float64) { - s.mu.Lock() - s.MovingAverage.Add(value) - s.mu.Unlock() -} - -func (s *threadSafeMovingAverage) Value() float64 { - s.mu.Lock() - defer s.mu.Unlock() - return s.MovingAverage.Value() -} - -func (s *threadSafeMovingAverage) Set(value float64) { - s.mu.Lock() - s.MovingAverage.Set(value) - s.mu.Unlock() -} - -// NewThreadSafeMovingAverage converts provided ewma.MovingAverage -// into thread safe ewma.MovingAverage. -func NewThreadSafeMovingAverage(average ewma.MovingAverage) ewma.MovingAverage { - if tsma, ok := average.(*threadSafeMovingAverage); ok { - return tsma - } - return &threadSafeMovingAverage{MovingAverage: average} -} - -type medianWindow [3]float64 - -func (s *medianWindow) Len() int { return len(s) } -func (s *medianWindow) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s *medianWindow) Less(i, j int) bool { return s[i] < s[j] } - -func (s *medianWindow) Add(value float64) { - s[0], s[1] = s[1], s[2] - s[2] = value -} - -func (s *medianWindow) Value() float64 { - tmp := *s - sort.Sort(&tmp) - return tmp[1] -} - -func (s *medianWindow) Set(value float64) { - for i := 0; i < len(s); i++ { - s[i] = value - } -} - -// NewMedian is fixed last 3 samples median MovingAverage. -func NewMedian() ewma.MovingAverage { - return NewThreadSafeMovingAverage(new(medianWindow)) -} diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/name.go b/vendor/github.com/vbauerster/mpb/v6/decor/name.go deleted file mode 100644 index 3af311254..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/decor/name.go +++ /dev/null @@ -1,12 +0,0 @@ -package decor - -// Name decorator displays text that is set once and can't be changed -// during decorator's lifetime. -// -// `str` string to display -// -// `wcc` optional WC config -// -func Name(str string, wcc ...WC) Decorator { - return Any(func(Statistics) string { return str }, wcc...) -} diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/on_complete.go b/vendor/github.com/vbauerster/mpb/v6/decor/on_complete.go deleted file mode 100644 index f46b19aba..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/decor/on_complete.go +++ /dev/null @@ -1,37 +0,0 @@ -package decor - -// OnComplete returns decorator, which wraps provided decorator, with -// sole purpose to display provided message on complete event. -// -// `decorator` Decorator to wrap -// -// `message` message to display on complete event -// -func OnComplete(decorator Decorator, message string) Decorator { - d := &onCompleteWrapper{ - Decorator: decorator, - msg: message, - } - if md, ok := decorator.(*mergeDecorator); ok { - d.Decorator, md.Decorator = md.Decorator, d - return md - } - return d -} - -type onCompleteWrapper struct { - Decorator - msg string -} - -func (d *onCompleteWrapper) Decor(s Statistics) string { - if s.Completed { - wc := d.GetConf() - return wc.FormatMsg(d.msg) - } - return d.Decorator.Decor(s) -} - -func (d *onCompleteWrapper) Base() Decorator { - return d.Decorator -} diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/percentage.go b/vendor/github.com/vbauerster/mpb/v6/decor/percentage.go deleted file mode 100644 index f4922bb52..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/decor/percentage.go +++ /dev/null @@ -1,58 +0,0 @@ -package decor - -import ( - "fmt" - "io" - "strconv" - - "github.com/vbauerster/mpb/v6/internal" -) - -type percentageType float64 - -func (s percentageType) Format(st fmt.State, verb rune) { - var prec int - switch verb { - case 'd': - case 's': - prec = -1 - default: - if p, ok := st.Precision(); ok { - prec = p - } else { - prec = 6 - } - } - - io.WriteString(st, strconv.FormatFloat(float64(s), 'f', prec, 64)) - - if st.Flag(' ') { - io.WriteString(st, " ") - } - io.WriteString(st, "%") -} - -// Percentage returns percentage decorator. It's a wrapper of NewPercentage. -func Percentage(wcc ...WC) Decorator { - return NewPercentage("% d", wcc...) -} - -// NewPercentage percentage decorator with custom format string. -// -// format examples: -// -// format="%.1f" output: "1.0%" -// format="% .1f" output: "1.0 %" -// format="%d" output: "1%" -// format="% d" output: "1 %" -// -func NewPercentage(format string, wcc ...WC) Decorator { - if format == "" { - format = "% d" - } - f := func(s Statistics) string { - p := internal.Percentage(s.Total, s.Current, 100) - return fmt.Sprintf(format, percentageType(p)) - } - return Any(f, wcc...) -} diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/size_type.go b/vendor/github.com/vbauerster/mpb/v6/decor/size_type.go deleted file mode 100644 index e4b974058..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/decor/size_type.go +++ /dev/null @@ -1,109 +0,0 @@ -package decor - -import ( - "fmt" - "io" - "math" - "strconv" -) - -//go:generate stringer -type=SizeB1024 -trimprefix=_i -//go:generate stringer -type=SizeB1000 -trimprefix=_ - -const ( - _ib SizeB1024 = iota + 1 - _iKiB SizeB1024 = 1 << (iota * 10) - _iMiB - _iGiB - _iTiB -) - -// SizeB1024 named type, which implements fmt.Formatter interface. It -// adjusts its value according to byte size multiple by 1024 and appends -// appropriate size marker (KiB, MiB, GiB, TiB). -type SizeB1024 int64 - -func (self SizeB1024) Format(st fmt.State, verb rune) { - var prec int - switch verb { - case 'd': - case 's': - prec = -1 - default: - if p, ok := st.Precision(); ok { - prec = p - } else { - prec = 6 - } - } - - var unit SizeB1024 - switch { - case self < _iKiB: - unit = _ib - case self < _iMiB: - unit = _iKiB - case self < _iGiB: - unit = _iMiB - case self < _iTiB: - unit = _iGiB - case self <= math.MaxInt64: - unit = _iTiB - } - - io.WriteString(st, strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64)) - - if st.Flag(' ') { - io.WriteString(st, " ") - } - io.WriteString(st, unit.String()) -} - -const ( - _b SizeB1000 = 1 - _KB SizeB1000 = _b * 1000 - _MB SizeB1000 = _KB * 1000 - _GB SizeB1000 = _MB * 1000 - _TB SizeB1000 = _GB * 1000 -) - -// SizeB1000 named type, which implements fmt.Formatter interface. It -// adjusts its value according to byte size multiple by 1000 and appends -// appropriate size marker (KB, MB, GB, TB). -type SizeB1000 int64 - -func (self SizeB1000) Format(st fmt.State, verb rune) { - var prec int - switch verb { - case 'd': - case 's': - prec = -1 - default: - if p, ok := st.Precision(); ok { - prec = p - } else { - prec = 6 - } - } - - var unit SizeB1000 - switch { - case self < _KB: - unit = _b - case self < _MB: - unit = _KB - case self < _GB: - unit = _MB - case self < _TB: - unit = _GB - case self <= math.MaxInt64: - unit = _TB - } - - io.WriteString(st, strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64)) - - if st.Flag(' ') { - io.WriteString(st, " ") - } - io.WriteString(st, unit.String()) -} diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/sizeb1000_string.go b/vendor/github.com/vbauerster/mpb/v6/decor/sizeb1000_string.go deleted file mode 100644 index 3f32ef715..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/decor/sizeb1000_string.go +++ /dev/null @@ -1,41 +0,0 @@ -// Code generated by "stringer -type=SizeB1000 -trimprefix=_"; DO NOT EDIT. - -package decor - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[_b-1] - _ = x[_KB-1000] - _ = x[_MB-1000000] - _ = x[_GB-1000000000] - _ = x[_TB-1000000000000] -} - -const ( - _SizeB1000_name_0 = "b" - _SizeB1000_name_1 = "KB" - _SizeB1000_name_2 = "MB" - _SizeB1000_name_3 = "GB" - _SizeB1000_name_4 = "TB" -) - -func (i SizeB1000) String() string { - switch { - case i == 1: - return _SizeB1000_name_0 - case i == 1000: - return _SizeB1000_name_1 - case i == 1000000: - return _SizeB1000_name_2 - case i == 1000000000: - return _SizeB1000_name_3 - case i == 1000000000000: - return _SizeB1000_name_4 - default: - return "SizeB1000(" + strconv.FormatInt(int64(i), 10) + ")" - } -} diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/sizeb1024_string.go b/vendor/github.com/vbauerster/mpb/v6/decor/sizeb1024_string.go deleted file mode 100644 index 9fca66cc7..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/decor/sizeb1024_string.go +++ /dev/null @@ -1,41 +0,0 @@ -// Code generated by "stringer -type=SizeB1024 -trimprefix=_i"; DO NOT EDIT. - -package decor - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[_ib-1] - _ = x[_iKiB-1024] - _ = x[_iMiB-1048576] - _ = x[_iGiB-1073741824] - _ = x[_iTiB-1099511627776] -} - -const ( - _SizeB1024_name_0 = "b" - _SizeB1024_name_1 = "KiB" - _SizeB1024_name_2 = "MiB" - _SizeB1024_name_3 = "GiB" - _SizeB1024_name_4 = "TiB" -) - -func (i SizeB1024) String() string { - switch { - case i == 1: - return _SizeB1024_name_0 - case i == 1024: - return _SizeB1024_name_1 - case i == 1048576: - return _SizeB1024_name_2 - case i == 1073741824: - return _SizeB1024_name_3 - case i == 1099511627776: - return _SizeB1024_name_4 - default: - return "SizeB1024(" + strconv.FormatInt(int64(i), 10) + ")" - } -} diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/speed.go b/vendor/github.com/vbauerster/mpb/v6/decor/speed.go deleted file mode 100644 index 634edabfd..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/decor/speed.go +++ /dev/null @@ -1,171 +0,0 @@ -package decor - -import ( - "fmt" - "io" - "math" - "time" - - "github.com/VividCortex/ewma" -) - -// FmtAsSpeed adds "/s" to the end of the input formatter. To be -// used with SizeB1000 or SizeB1024 types, for example: -// -// fmt.Printf("%.1f", FmtAsSpeed(SizeB1024(2048))) -// -func FmtAsSpeed(input fmt.Formatter) fmt.Formatter { - return &speedFormatter{input} -} - -type speedFormatter struct { - fmt.Formatter -} - -func (self *speedFormatter) Format(st fmt.State, verb rune) { - self.Formatter.Format(st, verb) - io.WriteString(st, "/s") -} - -// EwmaSpeed exponential-weighted-moving-average based speed decorator. -// For this decorator to work correctly you have to measure each -// iteration's duration and pass it to the -// *Bar.DecoratorEwmaUpdate(time.Duration) method after each increment. -func EwmaSpeed(unit int, format string, age float64, wcc ...WC) Decorator { - var average ewma.MovingAverage - if age == 0 { - average = ewma.NewMovingAverage() - } else { - average = ewma.NewMovingAverage(age) - } - return MovingAverageSpeed(unit, format, NewThreadSafeMovingAverage(average), wcc...) -} - -// MovingAverageSpeed decorator relies on MovingAverage implementation -// to calculate its average. -// -// `unit` one of [0|UnitKiB|UnitKB] zero for no unit -// -// `format` printf compatible verb for value, like "%f" or "%d" -// -// `average` MovingAverage implementation -// -// `wcc` optional WC config -// -// format examples: -// -// unit=UnitKiB, format="%.1f" output: "1.0MiB/s" -// unit=UnitKiB, format="% .1f" output: "1.0 MiB/s" -// unit=UnitKB, format="%.1f" output: "1.0MB/s" -// unit=UnitKB, format="% .1f" output: "1.0 MB/s" -// -func MovingAverageSpeed(unit int, format string, average ewma.MovingAverage, wcc ...WC) Decorator { - if format == "" { - format = "%.0f" - } - d := &movingAverageSpeed{ - WC: initWC(wcc...), - average: average, - producer: chooseSpeedProducer(unit, format), - } - return d -} - -type movingAverageSpeed struct { - WC - producer func(float64) string - average ewma.MovingAverage - msg string -} - -func (d *movingAverageSpeed) Decor(s Statistics) string { - if !s.Completed { - var speed float64 - if v := d.average.Value(); v > 0 { - speed = 1 / v - } - d.msg = d.producer(speed * 1e9) - } - return d.FormatMsg(d.msg) -} - -func (d *movingAverageSpeed) EwmaUpdate(n int64, dur time.Duration) { - durPerByte := float64(dur) / float64(n) - if math.IsInf(durPerByte, 0) || math.IsNaN(durPerByte) { - return - } - d.average.Add(durPerByte) -} - -// AverageSpeed decorator with dynamic unit measure adjustment. It's -// a wrapper of NewAverageSpeed. -func AverageSpeed(unit int, format string, wcc ...WC) Decorator { - return NewAverageSpeed(unit, format, time.Now(), wcc...) -} - -// NewAverageSpeed decorator with dynamic unit measure adjustment and -// user provided start time. -// -// `unit` one of [0|UnitKiB|UnitKB] zero for no unit -// -// `format` printf compatible verb for value, like "%f" or "%d" -// -// `startTime` start time -// -// `wcc` optional WC config -// -// format examples: -// -// unit=UnitKiB, format="%.1f" output: "1.0MiB/s" -// unit=UnitKiB, format="% .1f" output: "1.0 MiB/s" -// unit=UnitKB, format="%.1f" output: "1.0MB/s" -// unit=UnitKB, format="% .1f" output: "1.0 MB/s" -// -func NewAverageSpeed(unit int, format string, startTime time.Time, wcc ...WC) Decorator { - if format == "" { - format = "%.0f" - } - d := &averageSpeed{ - WC: initWC(wcc...), - startTime: startTime, - producer: chooseSpeedProducer(unit, format), - } - return d -} - -type averageSpeed struct { - WC - startTime time.Time - producer func(float64) string - msg string -} - -func (d *averageSpeed) Decor(s Statistics) string { - if !s.Completed { - speed := float64(s.Current) / float64(time.Since(d.startTime)) - d.msg = d.producer(speed * 1e9) - } - - return d.FormatMsg(d.msg) -} - -func (d *averageSpeed) AverageAdjust(startTime time.Time) { - d.startTime = startTime -} - -func chooseSpeedProducer(unit int, format string) func(float64) string { - switch unit { - case UnitKiB: - return func(speed float64) string { - return fmt.Sprintf(format, FmtAsSpeed(SizeB1024(math.Round(speed)))) - } - case UnitKB: - return func(speed float64) string { - return fmt.Sprintf(format, FmtAsSpeed(SizeB1000(math.Round(speed)))) - } - default: - return func(speed float64) string { - return fmt.Sprintf(format, speed) - } - } -} diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/spinner.go b/vendor/github.com/vbauerster/mpb/v6/decor/spinner.go deleted file mode 100644 index 6871639db..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/decor/spinner.go +++ /dev/null @@ -1,21 +0,0 @@ -package decor - -var defaultSpinnerStyle = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"} - -// Spinner returns spinner decorator. -// -// `frames` spinner frames, if nil or len==0, default is used -// -// `wcc` optional WC config -func Spinner(frames []string, wcc ...WC) Decorator { - if len(frames) == 0 { - frames = defaultSpinnerStyle - } - var count uint - f := func(s Statistics) string { - frame := frames[count%uint(len(frames))] - count++ - return frame - } - return Any(f, wcc...) -} diff --git a/vendor/github.com/vbauerster/mpb/v6/doc.go b/vendor/github.com/vbauerster/mpb/v6/doc.go deleted file mode 100644 index 5ada71774..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package mpb is a library for rendering progress bars in terminal applications. -package mpb diff --git a/vendor/github.com/vbauerster/mpb/v6/go.mod b/vendor/github.com/vbauerster/mpb/v6/go.mod deleted file mode 100644 index d9dad966e..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/go.mod +++ /dev/null @@ -1,11 +0,0 @@ -module github.com/vbauerster/mpb/v6 - -require ( - github.com/VividCortex/ewma v1.2.0 - github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/mattn/go-runewidth v0.0.12 - github.com/rivo/uniseg v0.2.0 - golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 -) - -go 1.14 diff --git a/vendor/github.com/vbauerster/mpb/v6/go.sum b/vendor/github.com/vbauerster/mpb/v6/go.sum deleted file mode 100644 index 08ee82f4e..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/go.sum +++ /dev/null @@ -1,11 +0,0 @@ -github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= -github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= -github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= -github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= -github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 h1:hZR0X1kPW+nwyJ9xRxqZk1vx5RUObAPBdKVvXPDUH/E= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/vendor/github.com/vbauerster/mpb/v6/internal/percentage.go b/vendor/github.com/vbauerster/mpb/v6/internal/percentage.go deleted file mode 100644 index a8ef8be12..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/internal/percentage.go +++ /dev/null @@ -1,19 +0,0 @@ -package internal - -import "math" - -// Percentage is a helper function, to calculate percentage. -func Percentage(total, current int64, width int) float64 { - if total <= 0 { - return 0 - } - if current >= total { - return float64(width) - } - return float64(int64(width)*current) / float64(total) -} - -// PercentageRound same as Percentage but with math.Round. -func PercentageRound(total, current int64, width int) float64 { - return math.Round(Percentage(total, current, width)) -} diff --git a/vendor/github.com/vbauerster/mpb/v6/internal/predicate.go b/vendor/github.com/vbauerster/mpb/v6/internal/predicate.go deleted file mode 100644 index 1e4dd24d9..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/internal/predicate.go +++ /dev/null @@ -1,6 +0,0 @@ -package internal - -// Predicate helper for internal use. -func Predicate(pick bool) func() bool { - return func() bool { return pick } -} diff --git a/vendor/github.com/vbauerster/mpb/v6/internal/width.go b/vendor/github.com/vbauerster/mpb/v6/internal/width.go deleted file mode 100644 index 216320f24..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/internal/width.go +++ /dev/null @@ -1,10 +0,0 @@ -package internal - -// CheckRequestedWidth checks that requested width doesn't overflow -// available width -func CheckRequestedWidth(requested, available int) int { - if requested <= 0 || requested >= available { - return available - } - return requested -} diff --git a/vendor/github.com/vbauerster/mpb/v6/priority_queue.go b/vendor/github.com/vbauerster/mpb/v6/priority_queue.go deleted file mode 100644 index 29d9bd5a8..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/priority_queue.go +++ /dev/null @@ -1,32 +0,0 @@ -package mpb - -// A priorityQueue implements heap.Interface -type priorityQueue []*Bar - -func (pq priorityQueue) Len() int { return len(pq) } - -func (pq priorityQueue) Less(i, j int) bool { - return pq[i].priority < pq[j].priority -} - -func (pq priorityQueue) Swap(i, j int) { - pq[i], pq[j] = pq[j], pq[i] - pq[i].index = i - pq[j].index = j -} - -func (pq *priorityQueue) Push(x interface{}) { - s := *pq - bar := x.(*Bar) - bar.index = len(s) - s = append(s, bar) - *pq = s -} - -func (pq *priorityQueue) Pop() interface{} { - s := *pq - *pq = s[0 : len(s)-1] - bar := s[len(s)-1] - bar.index = -1 // for safety - return bar -} diff --git a/vendor/github.com/vbauerster/mpb/v6/progress.go b/vendor/github.com/vbauerster/mpb/v6/progress.go deleted file mode 100644 index 5a3f96243..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/progress.go +++ /dev/null @@ -1,412 +0,0 @@ -package mpb - -import ( - "bytes" - "container/heap" - "context" - "fmt" - "io" - "io/ioutil" - "log" - "math" - "os" - "sync" - "time" - - "github.com/vbauerster/mpb/v6/cwriter" - "github.com/vbauerster/mpb/v6/decor" -) - -const ( - // default RefreshRate - prr = 120 * time.Millisecond -) - -// Progress represents a container that renders one or more progress -// bars. -type Progress struct { - ctx context.Context - uwg *sync.WaitGroup - cwg *sync.WaitGroup - bwg *sync.WaitGroup - operateState chan func(*pState) - done chan struct{} - refreshCh chan time.Time - once sync.Once - dlogger *log.Logger -} - -// pState holds bars in its priorityQueue. It gets passed to -// *Progress.serve(...) monitor goroutine. -type pState struct { - bHeap priorityQueue - heapUpdated bool - pMatrix map[int][]chan int - aMatrix map[int][]chan int - barShutdownQueue []*Bar - - // following are provided/overrided by user - idCount int - reqWidth int - popCompleted bool - outputDiscarded bool - rr time.Duration - uwg *sync.WaitGroup - externalRefresh <-chan interface{} - renderDelay <-chan struct{} - shutdownNotifier chan struct{} - parkedBars map[*Bar]*Bar - output io.Writer - debugOut io.Writer -} - -// New creates new Progress container instance. It's not possible to -// reuse instance after *Progress.Wait() method has been called. -func New(options ...ContainerOption) *Progress { - return NewWithContext(context.Background(), options...) -} - -// NewWithContext creates new Progress container instance with provided -// context. It's not possible to reuse instance after *Progress.Wait() -// method has been called. -func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress { - s := &pState{ - bHeap: priorityQueue{}, - rr: prr, - parkedBars: make(map[*Bar]*Bar), - output: os.Stdout, - debugOut: ioutil.Discard, - } - - for _, opt := range options { - if opt != nil { - opt(s) - } - } - - p := &Progress{ - ctx: ctx, - uwg: s.uwg, - cwg: new(sync.WaitGroup), - bwg: new(sync.WaitGroup), - operateState: make(chan func(*pState)), - done: make(chan struct{}), - dlogger: log.New(s.debugOut, "[mpb] ", log.Lshortfile), - } - - p.cwg.Add(1) - go p.serve(s, cwriter.New(s.output)) - return p -} - -// AddBar creates a bar with default bar filler. Different filler can -// be choosen and applied via `*Progress.Add(...) *Bar` method. -func (p *Progress) AddBar(total int64, options ...BarOption) *Bar { - return p.Add(total, NewBarFiller(BarDefaultStyle), options...) -} - -// AddSpinner creates a bar with default spinner filler. Different -// filler can be choosen and applied via `*Progress.Add(...) *Bar` -// method. -func (p *Progress) AddSpinner(total int64, alignment SpinnerAlignment, options ...BarOption) *Bar { - return p.Add(total, NewSpinnerFiller(SpinnerDefaultStyle, alignment), options...) -} - -// Add creates a bar which renders itself by provided filler. -// If `total <= 0` trigger complete event is disabled until reset with *bar.SetTotal(int64, bool). -// Panics if *Progress instance is done, i.e. called after *Progress.Wait(). -func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar { - if filler == nil { - filler = BarFillerFunc(func(io.Writer, int, decor.Statistics) {}) - } - p.bwg.Add(1) - result := make(chan *Bar) - select { - case p.operateState <- func(ps *pState) { - bs := ps.makeBarState(total, filler, options...) - bar := newBar(p, bs) - if bs.runningBar != nil { - bs.runningBar.noPop = true - ps.parkedBars[bs.runningBar] = bar - } else { - heap.Push(&ps.bHeap, bar) - ps.heapUpdated = true - } - ps.idCount++ - result <- bar - }: - bar := <-result - bar.subscribeDecorators() - return bar - case <-p.done: - p.bwg.Done() - panic(fmt.Sprintf("%T instance can't be reused after it's done!", p)) - } -} - -func (p *Progress) dropBar(b *Bar) { - select { - case p.operateState <- func(s *pState) { - if b.index < 0 { - return - } - heap.Remove(&s.bHeap, b.index) - s.heapUpdated = true - }: - case <-p.done: - } -} - -func (p *Progress) setBarPriority(b *Bar, priority int) { - select { - case p.operateState <- func(s *pState) { - if b.index < 0 { - return - } - b.priority = priority - heap.Fix(&s.bHeap, b.index) - }: - case <-p.done: - } -} - -// UpdateBarPriority same as *Bar.SetPriority(int). -func (p *Progress) UpdateBarPriority(b *Bar, priority int) { - p.setBarPriority(b, priority) -} - -// BarCount returns bars count. -func (p *Progress) BarCount() int { - result := make(chan int, 1) - select { - case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }: - return <-result - case <-p.done: - return 0 - } -} - -// Wait waits for all bars to complete and finally shutdowns container. -// After this method has been called, there is no way to reuse *Progress -// instance. -func (p *Progress) Wait() { - if p.uwg != nil { - // wait for user wg - p.uwg.Wait() - } - - // wait for bars to quit, if any - p.bwg.Wait() - - p.once.Do(p.shutdown) - - // wait for container to quit - p.cwg.Wait() -} - -func (p *Progress) shutdown() { - close(p.done) -} - -func (p *Progress) serve(s *pState, cw *cwriter.Writer) { - defer p.cwg.Done() - - p.refreshCh = s.newTicker(p.done) - - for { - select { - case op := <-p.operateState: - op(s) - case <-p.refreshCh: - if err := s.render(cw); err != nil { - p.dlogger.Println(err) - } - case <-s.shutdownNotifier: - if s.heapUpdated { - if err := s.render(cw); err != nil { - p.dlogger.Println(err) - } - } - return - } - } -} - -func (s *pState) newTicker(done <-chan struct{}) chan time.Time { - ch := make(chan time.Time) - if s.shutdownNotifier == nil { - s.shutdownNotifier = make(chan struct{}) - } - go func() { - if s.renderDelay != nil { - <-s.renderDelay - } - var internalRefresh <-chan time.Time - if !s.outputDiscarded { - if s.externalRefresh == nil { - ticker := time.NewTicker(s.rr) - defer ticker.Stop() - internalRefresh = ticker.C - } - } else { - s.externalRefresh = nil - } - for { - select { - case t := <-internalRefresh: - ch <- t - case x := <-s.externalRefresh: - if t, ok := x.(time.Time); ok { - ch <- t - } else { - ch <- time.Now() - } - case <-done: - close(s.shutdownNotifier) - return - } - } - }() - return ch -} - -func (s *pState) render(cw *cwriter.Writer) error { - if s.heapUpdated { - s.updateSyncMatrix() - s.heapUpdated = false - } - syncWidth(s.pMatrix) - syncWidth(s.aMatrix) - - tw, err := cw.GetWidth() - if err != nil { - tw = s.reqWidth - } - for i := 0; i < s.bHeap.Len(); i++ { - bar := s.bHeap[i] - go bar.render(tw) - } - - return s.flush(cw) -} - -func (s *pState) flush(cw *cwriter.Writer) error { - var lineCount int - bm := make(map[*Bar]struct{}, s.bHeap.Len()) - for s.bHeap.Len() > 0 { - b := heap.Pop(&s.bHeap).(*Bar) - cw.ReadFrom(<-b.frameCh) - if b.toShutdown { - if b.recoveredPanic != nil { - s.barShutdownQueue = append(s.barShutdownQueue, b) - b.toShutdown = false - } else { - // shutdown at next flush - // this ensures no bar ends up with less than 100% rendered - defer func() { - s.barShutdownQueue = append(s.barShutdownQueue, b) - }() - } - } - lineCount += b.extendedLines + 1 - bm[b] = struct{}{} - } - - for _, b := range s.barShutdownQueue { - if parkedBar := s.parkedBars[b]; parkedBar != nil { - parkedBar.priority = b.priority - heap.Push(&s.bHeap, parkedBar) - delete(s.parkedBars, b) - b.toDrop = true - } - if s.popCompleted && !b.noPop { - lineCount -= b.extendedLines + 1 - b.toDrop = true - } - if b.toDrop { - delete(bm, b) - s.heapUpdated = true - } - b.cancel() - } - s.barShutdownQueue = s.barShutdownQueue[0:0] - - for b := range bm { - heap.Push(&s.bHeap, b) - } - - return cw.Flush(lineCount) -} - -func (s *pState) updateSyncMatrix() { - s.pMatrix = make(map[int][]chan int) - s.aMatrix = make(map[int][]chan int) - for i := 0; i < s.bHeap.Len(); i++ { - bar := s.bHeap[i] - table := bar.wSyncTable() - pRow, aRow := table[0], table[1] - - for i, ch := range pRow { - s.pMatrix[i] = append(s.pMatrix[i], ch) - } - - for i, ch := range aRow { - s.aMatrix[i] = append(s.aMatrix[i], ch) - } - } -} - -func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOption) *bState { - bs := &bState{ - id: s.idCount, - priority: s.idCount, - reqWidth: s.reqWidth, - total: total, - filler: filler, - extender: func(r io.Reader, _ int, _ decor.Statistics) (io.Reader, int) { return r, 0 }, - debugOut: s.debugOut, - } - - if total > 0 { - bs.triggerComplete = true - } - - for _, opt := range options { - if opt != nil { - opt(bs) - } - } - - if bs.middleware != nil { - bs.filler = bs.middleware(filler) - bs.middleware = nil - } - - if s.popCompleted && !bs.noPop { - bs.priority = -(math.MaxInt32 - s.idCount) - } - - bs.bufP = bytes.NewBuffer(make([]byte, 0, 128)) - bs.bufB = bytes.NewBuffer(make([]byte, 0, 256)) - bs.bufA = bytes.NewBuffer(make([]byte, 0, 128)) - - return bs -} - -func syncWidth(matrix map[int][]chan int) { - for _, column := range matrix { - go maxWidthDistributor(column) - } -} - -var maxWidthDistributor = func(column []chan int) { - var maxWidth int - for _, ch := range column { - if w := <-ch; w > maxWidth { - maxWidth = w - } - } - for _, ch := range column { - ch <- maxWidth - } -} diff --git a/vendor/github.com/vbauerster/mpb/v6/proxyreader.go b/vendor/github.com/vbauerster/mpb/v6/proxyreader.go deleted file mode 100644 index 316f438d7..000000000 --- a/vendor/github.com/vbauerster/mpb/v6/proxyreader.go +++ /dev/null @@ -1,90 +0,0 @@ -package mpb - -import ( - "io" - "io/ioutil" - "time" -) - -type proxyReader struct { - io.ReadCloser - bar *Bar -} - -func (x *proxyReader) Read(p []byte) (int, error) { - n, err := x.ReadCloser.Read(p) - x.bar.IncrBy(n) - if err == io.EOF { - go x.bar.SetTotal(0, true) - } - return n, err -} - -type proxyWriterTo struct { - io.ReadCloser // *proxyReader - wt io.WriterTo - bar *Bar -} - -func (x *proxyWriterTo) WriteTo(w io.Writer) (int64, error) { - n, err := x.wt.WriteTo(w) - x.bar.IncrInt64(n) - if err == io.EOF { - go x.bar.SetTotal(0, true) - } - return n, err -} - -type ewmaProxyReader struct { - io.ReadCloser // *proxyReader - bar *Bar - iT time.Time -} - -func (x *ewmaProxyReader) Read(p []byte) (int, error) { - n, err := x.ReadCloser.Read(p) - if n > 0 { - x.bar.DecoratorEwmaUpdate(time.Since(x.iT)) - x.iT = time.Now() - } - return n, err -} - -type ewmaProxyWriterTo struct { - io.ReadCloser // *ewmaProxyReader - wt io.WriterTo // *proxyWriterTo - bar *Bar - iT time.Time -} - -func (x *ewmaProxyWriterTo) WriteTo(w io.Writer) (int64, error) { - n, err := x.wt.WriteTo(w) - if n > 0 { - x.bar.DecoratorEwmaUpdate(time.Since(x.iT)) - x.iT = time.Now() - } - return n, err -} - -func newProxyReader(r io.Reader, bar *Bar) io.ReadCloser { - rc := toReadCloser(r) - rc = &proxyReader{rc, bar} - - if wt, isWriterTo := r.(io.WriterTo); bar.hasEwmaDecorators { - now := time.Now() - rc = &ewmaProxyReader{rc, bar, now} - if isWriterTo { - rc = &ewmaProxyWriterTo{rc, wt, bar, now} - } - } else if isWriterTo { - rc = &proxyWriterTo{rc, wt, bar} - } - return rc -} - -func toReadCloser(r io.Reader) io.ReadCloser { - if rc, ok := r.(io.ReadCloser); ok { - return rc - } - return ioutil.NopCloser(r) -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 072b9661a..8a5cab431 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -5,7 +5,7 @@ github.com/Azure/go-ansiterm/winterm ## explicit github.com/BurntSushi/toml github.com/BurntSushi/toml/internal -# github.com/Microsoft/go-winio v0.5.1 +# github.com/Microsoft/go-winio v0.5.2 github.com/Microsoft/go-winio github.com/Microsoft/go-winio/backuptar github.com/Microsoft/go-winio/pkg/guid @@ -69,7 +69,7 @@ github.com/containerd/containerd/log github.com/containerd/containerd/pkg/userns github.com/containerd/containerd/platforms github.com/containerd/containerd/sys -# github.com/containerd/stargz-snapshotter/estargz v0.11.0 +# github.com/containerd/stargz-snapshotter/estargz v0.11.1 github.com/containerd/stargz-snapshotter/estargz github.com/containerd/stargz-snapshotter/estargz/errorutil # github.com/containernetworking/cni v1.0.1 @@ -153,7 +153,7 @@ github.com/containers/common/version # github.com/containers/conmon v2.0.20+incompatible ## explicit github.com/containers/conmon/runner/config -# github.com/containers/image/v5 v5.19.2-0.20220224100137-1045fb70b094 +# github.com/containers/image/v5 v5.19.2-0.20220302121925-9a9cd9322006 ## explicit github.com/containers/image/v5/copy github.com/containers/image/v5/directory @@ -232,7 +232,7 @@ github.com/containers/psgo/internal/dev github.com/containers/psgo/internal/host github.com/containers/psgo/internal/proc github.com/containers/psgo/internal/process -# github.com/containers/storage v1.38.3-0.20220214113600-26c561f9a645 +# github.com/containers/storage v1.38.3-0.20220301151551-d06b0f81c0aa ## explicit github.com/containers/storage github.com/containers/storage/drivers @@ -312,7 +312,7 @@ github.com/digitalocean/go-libvirt/internal/go-xdr/xdr2 github.com/digitalocean/go-qemu/qmp # github.com/disiqueira/gotree/v3 v3.0.2 github.com/disiqueira/gotree/v3 -# github.com/docker/distribution v2.8.0+incompatible +# github.com/docker/distribution v2.8.1+incompatible ## explicit github.com/docker/distribution github.com/docker/distribution/digestset @@ -683,13 +683,8 @@ github.com/ulikunitz/xz/lzma github.com/vbatts/tar-split/archive/tar github.com/vbatts/tar-split/tar/asm github.com/vbatts/tar-split/tar/storage -# github.com/vbauerster/mpb/v6 v6.0.4 -## explicit -github.com/vbauerster/mpb/v6 -github.com/vbauerster/mpb/v6/cwriter -github.com/vbauerster/mpb/v6/decor -github.com/vbauerster/mpb/v6/internal # github.com/vbauerster/mpb/v7 v7.3.2 +## explicit github.com/vbauerster/mpb/v7 github.com/vbauerster/mpb/v7/cwriter github.com/vbauerster/mpb/v7/decor @@ -881,9 +876,9 @@ gopkg.in/square/go-jose.v2/json # gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 gopkg.in/tomb.v1 # gopkg.in/yaml.v2 v2.4.0 +## explicit gopkg.in/yaml.v2 # gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b -## explicit gopkg.in/yaml.v3 # k8s.io/api v0.22.5 ## explicit |